Основное различие между монадическим и аппликативным синтаксическим анализом заключается в том, как обрабатывается последовательная композиция.В случае аппликативного парсера мы используем (<*>)
, тогда как с монадой мы используем (>>=)
.
(<*>) :: Parser (a -> b) -> Parser a -> Parser b
(>>=) :: Parser a -> (a -> Parser b) -> Parser b
Монадический подход более гибок, поскольку позволяет грамматике второй частизависит от результата первого, но нам редко нужна эта дополнительная гибкость на практике.
Вы можете подумать, что наличие некоторой дополнительной гибкости не может повредить, но в действительности это может быть.Это мешает нам выполнять полезный статический анализ парсера без его запуска.Например, скажем, мы хотим знать, может ли парсер соответствовать пустой строке или нет, и какие могут быть первые первые символы в совпадении.Нам нужны функции
empty :: Parser a -> Bool
first :: Parser a -> Set Char
С помощью аппликативного парсера мы легко можем ответить на этот вопрос.(Я немного обманываю здесь. Представьте, что у нас есть конструкторы данных, соответствующие (<*>)
и (>>=)
в наших «синтаксических» синтаксических анализаторах).
empty (f <*> x) = empty f && empty x
first (f <*> x) | empty f = first f `union` first x
| otherwise = first f
Однако, с монадическим парсером мы неНе знаю, что такое грамматика второй части, не зная ввода.
empty (x >>= f) = empty x && empty (f ???)
first (x >>= f) | empty x = first x `union` first (f ???)
| otherwise = first x
Допуская больше, мы можем рассуждать меньше.Это похоже на выбор между динамическими и статическими системами типов.
Но какой в этом смысл?Для чего мы можем использовать эти дополнительные статические знания?Ну, например, мы можем использовать его, чтобы избежать обратного отслеживания при разборе LL (1), сравнивая следующий символ с набором first
каждой альтернативы.Мы также можем статически определить, будет ли это двусмысленным, проверив, перекрываются ли наборы first
из двух альтернатив.
Другой пример - это то, что его можно использовать для восстановления после ошибок, как показано в документе Deterministic, Исправляющие ошибки синтаксические парсеры от S. Doaitse Swierstra и Luc Duponcheel.
Обычно, однако, выбор между аппликативным и монадическим синтаксическим анализом уже сделан авторами используемой библиотеки синтаксического анализа.,Когда библиотека, такая как Parsec, предоставляет оба интерфейса, выбор того, какой использовать, является чисто стилистическим.В некоторых случаях аппликативный код легче читать, чем монадический код, а иногда наоборот.