给出以下代码:
import Data.Word
data T = T deriving (Eq, Show)
class C a where f :: a -> ()
instance C T where f _ = ()
instance C Word16 where f _ = ()
main = return $ f 0x16
GHC抱怨它无法推断出文字0x16
的类型应该是错误:
No instance for (Num a0) arising from the literal ‘22’
The type variable ‘a0’ is ambiguous
很容易理解为什么会这样--Haskell允许数字文字属于任何具有Num
实例的类型,在这里我们无法消除文字{{1 (或22)应该是。
作为一个人类阅读也清楚我想要做的事情 - 只有一个类0x16
的可用实例满足C
约束,所以显然我打算使用那个因此Num
应被视为0x16
。
我知道有两种方法可以修复它:使用其类型注释文字:
Word16
或定义一个基本上为您做注释的函数:
main = return $ f (0x16 :: Word16)
我尝试了第三种方法,将w16 x = x :: Word16
main = return $ f (w16 0x16)
放在文件的顶部,希望Haskell选择它作为数字文字的默认类型,但我想我误解了{{1应该关键字,因为它不起作用。
我理解类型类是开放的,所以只是因为你可以在上面引用的上下文中做出假设default (Word16)
是default
的唯一数字实例,可能在其他模块中没有。但我的问题是:是否有一些机制可以假设/强制执行该属性,因此可以使用Word16
并使Haskell将其数字参数的类型解析为C
而无需显式注释在通话现场?
上下文是我正在实现一个EDSL,当我知道我的参数将是f
或其他非数字类型时,我宁愿不必包含手动类型提示。如果它让EDSL感觉更自然,我会对一些肮脏的类型/扩展滥用开放!虽然如果解决方案确实涉及顽皮的pragma,我肯定会欣赏在使用它们时我应该警惕的暗示。
答案 0 :(得分:5)
对于其他任何想知道default
的人(我知道我是!)
https://www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-750004.3
引用第4.3.4节:
在发现模糊类型的情况下,如果出现以下情况,则模糊类型变量v是可以违约的:
- v仅出现在C v形式的约束中,其中C是一个类,
- 这些类中至少有一个是数字类(即Num或Num的子类),
- 所有这些类都在Prelude或标准库中定义。
这就解释了为什么你的default
子句被完全忽略了; C
不是标准的库类型类。
(至于为什么这是规则......在那里无法帮助你。大概是为了避免破坏任意用户定义的代码。)