Типичная подпись функции "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 будет работать с вашим типа.