Сигнатура типа функции Parsec 'parse' и класса 'Stream' - PullRequest
10 голосов
/ 16 июня 2011

Что означает ограничение (Stream s Identity t) в следующем объявлении типа?

parse :: (Stream s Identity t)
  => Parsec s () a -> SourceName -> s -> Either ParseError a

Что такое Stream в следующем объявлении класса, что оно означает.Я полностью потерян.

class Monad m => Stream s m t | s -> t where

Когда я использую Parsec, я все время сталкиваюсь с типографскими сигнатурами (xxx :: yyy).Я всегда пропускаю подписи, загружаю src в ghci, а затем копирую подпись типа обратно в мой файл .hs.Это работает, но я до сих пор не понимаю, что все эти подписи.


РЕДАКТИРОВАТЬ: подробнее о сути моего вопроса.

Явсе еще запутался в «контексте» подписи типа:

(Show a) =>

означает, что a должен быть экземпляром класса Show.

(Stream s Identity t) => 

в чем смысл этого 'context ', так как t никогда не показывался после =>


У меня много разных парсеров для запуска, поэтому я пишу функцию деформации для запуска любого из этих парсеров с реальными файлами.но здесь возникает проблема:

Вот мой код, он не может быть загружен, как я могу заставить его работать?

module RunParse where
import System.IO
import Data.Functor.Identity (Identity)
import Text.Parsec.Prim (Parsec, parse, Stream)

--what should I write "runIOParse :: ..."
--runIOParse :: (Stream s Identity t, Show a) => Parsec s () a -> String -> IO ()
runIOParse pa filename =
  do
    inh <- openFile filename ReadMode
    outh <- openFile (filename ++ ".parseout") WriteMode
    instr <- hGetContents inh
    let result = show $ parse pa filename instr
    hPutStr outh result
    hClose inh
    hClose outh

Ответы [ 2 ]

12 голосов
/ 16 июня 2011

ограничение: (Идентификатор потока t) означает, что?

Это означает, что вход s, на котором работает ваш анализатор (т.е. [Char]), должен быть экземпляромStream класс.В документации вы видите, что [Char] действительно является экземпляром Stream, поскольку любой список имеет вид.

Параметр t - это тип токена, который обычно равен Char и определяется с помощью s, что указывает на функциональную зависимость s -> t.

Но не стоит слишком беспокоиться об этом классе типов Stream.Он используется только для того, чтобы иметь единый интерфейс для любого типа потока, например списков или ByteStrings.

что такое Stream

Stream - это просто класс типов.Он имеет функцию uncons, которая возвращает головку ввода и хвост в кортеже, заключенном в Maybe.Обычно вам не понадобится эта функция.Насколько я вижу, он нужен только в самых базовых синтаксических анализаторах, таких как tokenPrimEx.

Edit:

что означает этот контекст', поскольку t никогда не показывалось после =>

Посмотрите на функциональные зависимости .t никогда не отображается после ´ => ´, потому что оно определяется s.А это значит, что вы можете использовать uncons на любом s.

Вот мой код, он не может быть загружен, как я могу заставить его работать?

Простой: Добавить оператор импорта для Text.Parsec.String, который определяет отсутствующийэкземпляр для Stream [tok] m tok.Здесь документация может быть немного понятнее, потому что выглядит так, как будто этот экземпляр был определен в Text.Parsec.Prim.

Альтернативно импортируйте всю библиотеку Parsec (import Text.Parsec) - так я всегда делаю.

11 голосов
/ 16 июня 2011

Класс типа Stream является абстракцией для спискообразных структур данных. Ранние версии Parsec работали только для анализа списков токенов (например, String - это синоним [Char], поэтому Char - это тип токена), что может быть очень неэффективным представлением. В наши дни наиболее существенный ввод в Haskell обрабатывается как типы Text или ByteString, которые не являются списками, но могут очень похожи на них.

Так, например, вы упомянули

parse :: (Stream s Identity t)
  => Parsec s () a -> SourceName -> s -> Either ParseError a

Некоторые специализации этого типа будут

parse1 :: Parsec String () a -> SourceName -> String -> Either ParseError a
parse2 :: Parsec Text () a -> SourceName -> Text -> Either ParseError a
parse3 :: Parsec ByteString () a -> SourceName -> ByteString -> Either ParseError a

или даже, если у вас есть отдельный лексер с типом токена MyToken:

parse4 :: Parsec [MyToken] () a -> SourceName -> [MyToken] -> Either ParseError a

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

Вы даже можете объявить свой собственный экземпляр Stream, поэтому, если ваш ввод относится к какому-либо другому типу, который действует как список, вы можете написать экземпляр, реализовать функцию uncons, и Parsec будет работать с вашим типа, а также.

...