Parsec函数'parse'的类型签名和类'Stream'

时间:2011-06-16 09:59:11

标签: haskell types parsec

约束(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) => 

这个'上下文'的含义是什么,因为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

2 个答案:

答案 0 :(得分:12)

  

约束:( Stream s Identity t)意味着什么?

这意味着您的解析器工作的输入s(即[Char])必须是Stream类的实例。在documentation中,您看到[Char]确实是Stream的一个实例,因为任何列表都是。

参数t是令牌类型,通常为Char,并由s 确定,表示功能依赖s -> t

但是不要过多担心这个Stream类型类。它仅用于具有任何类似Stream的类型的统一接口,例如列表或字节串。

  

什么是Stream

Stream只是一个类型类。它具有uncons函数,它返回输入的头部和尾部,包含在Maybe中的元组中。通常你不需要这个功能。据我所知,它只需要最基本的解析器,如tokenPrimEx

修改

  

这个'上下文'的含义是什么,因为在=&gt;

之后t从未显示过

看看functional dependenciest永远不会在'=&gt;'之后显示,因为它由s决定。 这意味着您可以uncons使用s

  

这是我的代码,无法加载,我该如何使其工作?

简单:为Text.Parsec.String添加导入语句,该语句定义Stream [tok] m tok的缺失实例。这里的文档可能更清晰一些,因为它看起来好像是Text.Parsec.Prim中定义了这个实例。

或者导入整个Parsec库(import Text.Parsec) - 这就是我一直这样做的。

答案 1 :(得分:11)

Stream类型类是类似列表的数据结构的抽象。早期版本的Parsec仅用于解析令牌列表(例如,String[Char]的同义词,因此Char是令牌类型),这可能是一种效率非常低的表示。这些天,Haskell中的大多数实质性输入都被处理为TextByteString类型,这些类型不是列表,但可以像它们一样行事。

所以,例如,你提到

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也适用于你的类型。