Парсеры Parsec имеют довольно сложные типы из-за их гибкости. Вы можете обойти это двумя способами:
Поместите {-# Language FlexibleContexts #-}
в начало вашего исходного файла. Эта прагма говорит GHC сделать дополнительный вывод типа.
(Рекомендуется) Присвойте char1 явный тип.
char1 :: Char -> Parsec String () Char
char1 c = char c
Это рекомендуется, потому что вы всегда должны давать любому объявлению верхнего уровня явный тип. Если вы этого не сделаете, то все, что компилятор может вам сказать, это то, что где-то в вашем коде есть несоответствие типов (оно сообщит вам, где было обнаружено противоречие, но, скорее всего, это не та ошибка). При явном объявлении типа компилятор может сузить его.
В этом случае Parsec определяет тип Parsec
как
type Parsec s u = ParsecT s u Identity
Вы узнаете ParsecT
из сообщения об ошибке. ParsecT
- это преобразователь монад: он позволяет парсеру делать вещи в других монадах (например, IO), когда он распознает текст. В этом случае нам нужен только парсер, поэтому мы используем монаду Identity
, которая ничего не делает.
Параметр s
является потоком входных данных. В этом случае мы скажем, что парсер получает данные от String
.
Параметр u
является состоянием. Это позволяет вам делать такие вещи, как запись имен переменных в таблицу символов по мере их появления. В этом случае мы не используем состояние, поэтому его просто ()
.
Если бы вы писали настоящий синтаксический анализатор, вы обычно определяли бы свой собственный синоним типа, например,
type FooParser a = Parsec Text FooState a
char1 :: Char -> FooParser Char
char1 = char