Haskell特设多态性
我试图了解haskell中的特殊多态性,即具有相同的函数为不同的参数类型提供不同的行为 但是当下面的测试代码编译时Haskell特设多态性,haskell,polymorphism,overloading,typeclass,Haskell,Polymorphism,Overloading,Typeclass,我试图了解haskell中的特殊多态性,即具有相同的函数为不同的参数类型提供不同的行为 但是当下面的测试代码编译时 {-# 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
{-# 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
如果我试着用类似
foo True
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
但是,如果我指定了返回类型,它将起作用:
foo True :: Int -- gives 0
为什么需要这样做?Bool的参数类型应足以解决歧义
另外:这是实现类似行为的“最佳”方式吗?(没有将函数重命名为
fooBool
和foodoull
)您面临的问题是,重载由类中的所有类型决定,包括仅显示为返回类型的类型。您可以同时拥有MyClass Bool Int
和MyClass Bool String
的实例,并且可以根据预期的类型消除歧义
Haskell TypeClass的核心设计权衡之一是“开放世界假设”。Haskell类型实例是隐式全局的:特定类型(在本例中为类型序列)在整个程序中只能有一个实例,该实例隐式导出到使用该类型的所有模块
这使得在没有意识到的情况下获取某个类的新实例变得非常容易,因此Haskell typechecker假设实例可能存在于任何有效的类型组合中。在您的例子中,这意味着虽然MyClass Bool Int
是使用Bool
的唯一实例,但它与其他可能的MyClass Bool b
实例仍然不明确
一旦为整个表达式的类型添加注释,它就不再是不明确的,因为a
和b
都是固定的
要获得预期的行为,可以使用。这些允许您指定对于任何给定的a
,只有一个可能的b
,这将使GHC正确推断类型。它看起来像这样:
class MyClass a b | a -> b where
当然,这确实有意地抛弃了一些灵活性:现在您不能同时拥有
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
我们在这里使用。我喜欢这些显式类型级别的函数,但如果您不喜欢,可以使用FundeP,因为它们相当微妙。老实说:我不会从
MultiParamTypeClasses开始,但在您的情况下,您可能也想看看;)(如果不清楚,请按照链接进行操作-问题在这里得到了很好的解释-包括一个可能的解决方案-extenstion说)对于这个任务,我将使用它,而不是函数依赖项。@user3237465将其添加为回答谢谢,这为我澄清了一些事情。但是,即使具有功能依赖性,调用foo 1.73也将失败。你也能解释一下吗?if将失败,因为1.73
本身不是Double
,而是更通用的fractive
——所以你必须执行foo(1.73::Double)
或修改foo
实例以获得分数a
@wonce:这里的问题是1.73
本身是多态的:它可以是任何分数
类型,如浮点
、双精度
或理性
。因此,在使用哪一个实例时可能会出现歧义。