GHCI不能在编译时推断Eq类,但在运行时运行良好吗?

时间:2013-07-22 04:55:50

标签: haskell

很抱歉这个令人困惑的标题。我正在Haskell中编写一个解析器组合库以获得乐趣。以下是所有(我认为!)相关的类型注释和定义:

data Parser a = Parser (State -> Reply a)

parse :: Parser a -> [Char] -> Either ParseError a

nil :: Parser [a]
nil = Parser $ \state -> Ok [] state

基本上,parse函数应用Parser包围当前状态的函数,如果解析成功,则将结果包装在Either中。 nil解析器采用状态并返回空列表的成功解析。所以我们应该拥有,

parse nil "dog" == Right []

事实上,如果我只是加载所有这些存活的模块,那么它将编译并评估为True。

我实际上是在尝试对库运行一些QuickCheck测试,所以我写了这个:

import Parsimony
import Test.QuickCheck

prop_nil :: [Char] -> Bool
prop_nil xs = parse nil xs == Right []

这无法编译!它会引发以下错误:

No instance for (Eq a0) arising from a use of `=='
The type variable `a0' is ambiguous

此时我很困惑为什么表达式在评估时可以正常工作,但无法在参数化版本中编译。

3 个答案:

答案 0 :(得分:9)

由于nil是多态的,Right []也是多态的,GHC的表达式为Bool,但中间有一些未绑定的类型变量。由于GHC不知道使用什么样的具体类型,因此GHC过时并且死亡。 GHCi无论好坏,都会因为其违约规则而推断[()]或类似的东西。这是ghci奇怪的怪癖之一,它会自动默认类型变量。

要解决此问题,只需强行手动绑定a

-- It's important that whatever you force it to actually is comparable
-- eg there should be an instance like
instance Eq ParseError where
-- Otherwise you're kinda stuck.

prop_nil xs = parse nil xs == (Right xs :: Either ParseError String)

PS我喜欢解析器库的名字Parsimony,祝你好运!

答案 1 :(得分:7)

问题是nil的类型是Parser [a]。因此parse nil xs属于Either ParseError [a]类型。 Right []最常见的类型为Either l [a];将其与parse nil xs进行比较会强制lParseError,但列表中的类型仍然完全不受约束。没有任何更多的背景,它仍然是完全多态的; a不一定是Eq类型类的成员,即使它没有办法知道用于实现==的实例,所以它不是'{1}}有效在这两个条款上调用==

在一个真实的程序中,你可能会因为你使用结果来保存你的东西,这会强迫特定事件与你使用它的任何东西一致对于。这可能是一些具有Eq实现的具体类型。

当你谈到加载模块时,我认为你的意思是GHCI解释器。 GHCI增加了一些额外的违约规则。特别是它倾向于将无约束类型变量(不是顶级函数的类型)默认为(),因此它不必经常抱怨模糊类型变量。

GHCi中的交互式会话往往比完全编译的现实模块更经常遇到模糊类型变量 far ,因为它必须大部分独立地编译小片段。 GHCi扩展了默认规则以使这些工作更频繁地进行(虽然它通常只会在用户期望不同类型时将错误延迟到下一个引用,并且GHCi和GHC之间的差异经常导致混淆)。

测试片段可能会遇到类似的问题。如果您正在测试多态函数,那么您通常不会充分限制某些类型以使类型推断起作用,就像您真正有目的地使用函数一样。但是如果没有GHCi的扩展违约规则,这个问题就会在问题的位置显示为一个实际的模糊类型错误,而不是通过任意选择一个类型来掩盖它。

要解决此问题,您只需添加类型注释即可修复列表类型。声明完整类型的parse nil xsRight [],只需在右侧声明空列表文字的类型即可。像这样的Sometihng应该可以做到这一点:

prop_nil :: [Char] -> Bool
prop_nil xs = parse nil xs == Right ([] :: [Int])

答案 2 :(得分:3)

另一种方法是首先避免使用Eq约束:

prop_nil xs = either (const False) null (parse nil xs) 

或更明确的

prop_nil xs = case parse nil xs of
   Right [] -> True
   _        -> False
相关问题