Можно ли выразить цепочку l1 с помощью аппликатива?

#parsing #haskell #parsec

#синтаксический анализ #haskell

Вопрос:

Можно ли выразить chainl1 комбинатор из Parsec, не используя экземпляр Monad, определенный parsec?

 chainl1 p op =
  do x <- p
     rest x
  where
    rest x = do f <- op
                y <- p
                rest (f x y)
          <|> return x
  

Ответ №1:

Да, это так:

 chainl1 p op = foldl (flip ($)) <$> p <*> many (flip <$> op <*> p)
  

Идея в том, что вы должны проанализировать p (op p)* и оценить его как (...(((p) op p) op p)...) .

Это может помочь немного расширить определение:

 chainl1 p op = foldl (x f -> f x) <$> p <*> many ((f y -> flip f y) <$> op <*> p)
  

Поскольку пары op и p анализируются, результаты применяются немедленно, но поскольку p это правильный операнд op , ему нужен flip .

Итак, тип результата many (flip <$> op <*> p) f [a -> a] равен . Затем этот список функций применяется слева направо к начальному значению p by foldl .

Комментарии:

1. Не могли бы вы объяснить немного больше? Почему foldl и flip?

Ответ №2:

Уродливое, но эквивалентное Applicative определение:

 chainl1 p op =
    p <**>
    rest
  where
    rest = flip <$> op <*>
           p <**>
           pure (.) <*> rest
        <|> pure id
  

Вместо явной передачи аргумента левой стороны x в правую часть op , эта аппликативная форма «цепочки op » частично применяется к их аргументу правой стороны (следовательно flip <$> op <*> p ) через поднятый комбинатор (.) , а затем применяет крайний левый p путь (<**>) к результату rest :: Alternative f => f (a -> a) .

Комментарии:

1. Когда я пытаюсь сделать это с chainl1 (read <$> many digit) (( ) <$ char ' ' <|> (*) <$ char '*') помощью on "1 2*3 4" , я получаю 17 вместо 13.