Типичная подпись функции "parse" функции Parsec и класса "Stream"

Что означает ограничение (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 файл. Он работает, но я до сих пор не понимаю, что все эти подписи.


EDIT: больше о точке моего вопроса.

Я все еще запутался в "контексте" сигнатуры типа:

(Show a) =>

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

(Stream s Identity t) => 

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


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

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

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

Ответ 1

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

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

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

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

что такое Stream

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

Edit:

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

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

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

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

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

Ответ 2

Класс Stream type является абстракцией для структур данных, подобных спискам. Ранние версии 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 будет работать с вашим типа.