Parsec: Как просто создать новый парсер из одного в библиотеке - PullRequest
0 голосов
/ 08 ноября 2018

Предположим, я просто хочу создать свой собственный парсер, который точно такой же, как char в Parsec, но когда я запускаю

import Text.Parsec
char1 c = char c

Это дает мне

? Non type-variable argument in the constraint: Stream s m Char
  (Use FlexibleContexts to permit this)
? When checking the inferred type
    char1 :: forall s (m :: * -> *) u.
             Stream s m Char =>
             Char -> ParsecT s u m Cha

Как я могу решить это? Есть ли какой-то другой импорт, который я должен включить? Спасибо

1 Ответ

0 голосов
/ 08 ноября 2018

Парсеры Parsec имеют довольно сложные типы из-за их гибкости. Вы можете обойти это двумя способами:

  1. Поместите {-# Language FlexibleContexts #-} в начало вашего исходного файла. Эта прагма говорит GHC сделать дополнительный вывод типа.

  2. (Рекомендуется) Присвойте 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
...