2015-02-23 5 views
2

У меня есть следующий ADT:Проблем с Applicative стилем Parsec

type Program = [Expr] 
data Expr = 
    Num Int 
    | Bool Bool 
    | Binding String Expr 
    deriving (Show) 

Вот анализатор для переменных связывания выражений вида lhs is rhs.

binding :: Parser Expr 
binding = do 
    lhs <- word 
    spaces 
    string "is" 
    spaces 
    rhs <- expr 
    return $ Binding lhs rhs 

Это прекрасно работает, но когда я пытаюсь преобразовать его в аппликативный стиль, это дает неверный результат.

binding :: Parser Expr 
binding = Binding <$> word <* (spaces *> string "is" *> spaces) *> expr 

*> Замена с >> в скобках, часть не работает. В чем разница между этими двумя реализациями? Есть ли комбинатор для составления двух парсеров и игнорирования результата обоих?

Попытка отладки с Debug.trace тоже не работала ... Ничего не было напечатано.

binding :: Parser Expr 
binding = (\x y -> trace (show (x, y)) (Binding x y)) <$> word <* (spaces *> string "is" *> spaces) *> expr 

Остальная часть синтаксического анализа, для контекста:

word :: Parser String 
word = many1 letter 

expr :: Parser Expr 
expr = binding <|> atom 

program :: Parser Program 
program = do 
    spaces 
    result <- many (expr <* spaces) 
    return result 
+0

Быстро догадаться, но разве вы не должны использовать '(<*>)' после 'word' в вашем определении' binding'? I.e: 'binding = Binding <$> Слово <*> (пробелы *> строка" есть "*> пробелы) *> expr' – danem

+0

Это работает? Возможно, вы должны предоставить больше своего кода. http://lpaste.net/121008 – danem

+0

Вы заметили это быстро.:) Спасибо за помощь! – user1953221

ответ

10

Ваша проблема в том, что <$> и <*> и т. Д. Оставлены ассоциативными. Это означает, что ваша линия:

binding = Binding <$> word <* (spaces *> string "is" *> spaces) *> expr 

будет интерпретироваться как

binding = (Binding <$> word <* (spaces *> string "is" *> spaces)) *> expr 

Это означает, что он будет анализировать, а затем игнорировать все до последнего выраж. Как @icktoofay сказал, вы можете написать предполагаемую версию, как:

binding = Binding <$> word <* spaces <* string "is" <* spaces <*> expr 

и не нужны никакие скобки вообще, из-за левоассоциативность.

+0

Спасибо за объяснение! – user1953221

6

@danem правильно, попробуйте:

binding :: Parser Expr 
binding = Binding <$> word <*> (spaces *> string "is" *> spaces *> expr) 

Полный источник: http://lpaste.net/121011

Ваше первоначальное определение разборе таким образом:

binding = ((Binding <$> word) <* (spaces *> string "is" *> spaces)) *> expr 

т. Е. Имеет форму something *> expr, и поэтому возвращаемое значение определяется исключительно последним expr. Маркер lhs и is разобран, но затем отбрасывается.

Вот как проверить тип подвыражения:

Binding      :: String -> Expr -> Expr 
(Binding <$> word)   :: Parser (Expr -> Expr) 
(Binding <$> word) <* (...) :: Parser (Expr -> Expr) 

Итак, мы видим, что все проверки типа из-за выделки и тот факт, что мы отбрасывая результат something.

+1

Это выглядит правильно (хотя вы можете удалить круглые скобки с символом «Binding <$>». <* Spaces <* string "- это« <* spaces <*> expr'), но я думаю, добавив небольшое объяснение того, что было не так с попыткой решения и почему это работает сделало бы это более ценным ответом. – icktoofay

+0

Хорошая идея - я немного переработал ответ. – ErikR

+0

Интересное альтернативное решение! Это дало мне новое представление о том, как работают эти операторы. – user1953221

Смежные вопросы