Haskell中Parser从type改为data定义后编译错误问题问询
核心原理
type与data关键字的语义差异是本次编译失败的根本原因:
- 原定义中
type Parser a = String -> Maybe (a ,String)是类型别名,仅为已有函数类型起别名,Parser a和String -> Maybe (a ,String)完全等价,可直接混用- 新定义中
data Parser a = Parser { runParser :: String -> Maybe (a , String ) }是全新代数数据类型,Parser是类型构造器,同名的Parser是值构造器,用来包裹实际的解析函数,类型Parser a和内部的函数类型是完全独立的两个类型,不能直接混用
调整步骤
- 为所有解析器的实现套上
Parser值构造器
原代码中parseInt、parseChar直接返回的是符合类型签名的函数,修改后需要把整个函数逻辑用Parser构造器包裹,修改后代码如下:
- 为所有解析器的实现套上
parseInt :: Parser Int parseInt = Parser $ \s -> case s of "" -> Nothing _ -> case reads s :: [(Int, String)] of [(x, rest)] -> Just (x, rest) _ -> Nothing parseChar :: Char -> Parser Char parseChar a = Parser $ \str -> if a == head str then Just (a, tail str) else Nothing
- 调整解析器的调用方式
修改后Parser a类型的值不能直接作为函数调用,需要先通过runParser记录字段取出内部的解析函数再传入待解析字符串。示例:
原调用方式:parseInt "123test"
新调用方式:runParser parseInt "123test"
- 调整解析器的调用方式
- 配套调整Parser相关的类型类实现(如果有)
如果之前为Parser实现了Functor、Applicative、Monad等类型类实例,需要同步修改实例实现,操作Parser值时先通过runParser拆包获取内部函数,计算完成后再用Parser构造器打包返回。
- 配套调整Parser相关的类型类实现(如果有)
补充说明
改用data定义Parser相比类型别名更适合后续扩展:可以避免类型别名的类型检查逃逸问题,也可以为Parser自定义类型类实例,是实现Parser组合子的标准写法。
内容的提问来源于stack exchange,提问作者Jonathan Layduhur
相关产品推荐
相关产品推荐

