won*_*nce 4 polymorphism haskell overloading typeclass
我试图了解haskell中的ad-hoc多态性,即具有相同的函数为不同的参数类型提供不同的行为.
但是,以下测试代码编译
{-# LANGUAGE MultiParamTypeClasses #-}
class MyClass a b where
foo :: a -> b
instance MyClass Bool Int where
foo True = 0
foo False = 1
instance MyClass Double Double where
foo x = -x
Run Code Online (Sandbox Code Playgroud)
如果我尝试使用类似的东西来调用它
foo True
Run Code Online (Sandbox Code Playgroud)
ghci对我大吼:
No instance for (MyClass Bool b0) arising from a use of `foo'
The type variable `b0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there is a potential instance available:
instance MyClass Bool Int -- Defined at test.hs:6:10
Possible fix: add an instance declaration for (MyClass Bool b0)
In the expression: foo True
In an equation for `it': it = foo True
Run Code Online (Sandbox Code Playgroud)
但是,如果我指定返回类型,它的工作原理如下:
foo True :: Int -- gives 0
Run Code Online (Sandbox Code Playgroud)
为什么需要这个?Bool的参数类型应该足以解决歧义.
另外:这是实现类似行为的"最佳"方式吗?(不将函数重命名为fooBool
和fooDouble
)
Tik*_*vis 10
您遇到的问题是重载由类中的所有类型决定- 包括仅作为返回类型出现的类型.你可以有实例两个MyClass Bool Int
和MyClass Bool String
,这将是能够基于预计什么类型的歧义.
Haskell类型类的核心设计权衡之一是"开放世界假设".Haskell类型实例是隐式全局的:特定类型(或类型序列,在这种情况下)在整个程序中只能有一个实例,它使用该类型隐式导出到所有模块.
这使得它很容易得到一些类的新实例没有意识到这一点,所以哈斯克尔typechecker假设情况可能可能存在的任何类型的有效组合.在您的情况下,这意味着虽然MyClass Bool Int
是唯一使用的实例Bool
,但它仍然与其他可能的MyClass Bool b
实例不明确.
一旦你添加注释整个表达式的类型,它不再是模糊的,因为这两个a
和b
是固定的.
要获得您期望的行为,您可以使用FunctionalDependencies
.这些允许您指定任何给定的只有一个可能,这将使GHC正确推断类型.它看起来像这样:b
a
class MyClass a b | a -> b where
Run Code Online (Sandbox Code Playgroud)
当然,这不故意扔了一定的灵活性:现在你不能有两个实例MyClass Bool Int
和MyClass Bool String
.
Tikhon Jelvis详细阐述了这个问题,并建议使用功能依赖,但还有另一种选择:类型系列.你的代码变成了
{-# LANGUAGE TypeFamilies #-}
class MyClass a where
type R a
foo :: a -> R a
instance MyClass Bool where
type R Bool = Int
foo True = 0
foo False = 1
instance MyClass Double where
type R Double = Double
foo x = -x
Run Code Online (Sandbox Code Playgroud)
我们在这里使用关联类型同义词 我喜欢这些显式类型级别的函数,但如果你不喜欢,可以使用fundeps,因为差异相当微妙.