如何在没有注释的情况下明确地制作伪鸭型的这个例子

时间:2016-04-20 07:39:54

标签: haskell typeclass duck-typing

我想使用MultiParamTypeClasses演示在Haskell中使用静态可验证的鸭子类型的想法,但我在避免类型歧义方面遇到了麻烦。

以下是代码:

{-# LANGUAGE MultiParamTypeClasses #-}

class HasBar a b where
    bar :: b -> a 

data Foo = Foo { barBool :: Bool } deriving (Show)

instance HasBar Bool Foo where
    bar = barBool

data Bazz = Bazz { barInt :: Int } deriving (Show)

instance HasBar Int Bazz where
    bar = barInt

当我将其加载到GHCi并尝试执行bar (Foo True)bar (Bazz 5)时,我收到Non type-variable argument错误,并建议FlexibleContexts,这只会将错误更改为歧义错误。现在做False || bar (Foo True)之类的工作正常。但由于Foo只是返回Bool的类型类的成员,因此似乎不需要它。

似乎这个问题与以下内容的可能性有关:

instance HasBar Int Foo where
    bar = const 5

这使得这些类型变得模棱两可。但是,如果只有一个实例,我不明白为什么有任何问题阻止Haskell找到类型(我需要某种扩展)。如果我不能这样做,那么有MultiParamTypeClasses的替代方案只允许一个实例并允许这种伪鸭型的东西工作吗?

1 个答案:

答案 0 :(得分:4)

问题在于,它不仅要查找看到的内容,还能查找知道的内容 - 并且您有可能创建一个{ {1}}以及它抱怨

您可以使用FunctionalDependenciesTypeFamilies

摆脱这种情况

使用功能依赖

第一个扩展可能就是这里的方式(您不必更改大部分代码)。您基本上可以告诉GHCi,类/约束中的HasBar Int Foo类型足以决定类型b

如果您将其更改为:

a

它会起作用(仅在GHCi中需要{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-} class HasBar a b | b -> a where bar :: b -> a

FlexibleContexts

使用类型系列

如果您对此感兴趣,那么类型系列和相关类型也是如此:

λ> :set -XFlexibleContexts
λ> bar (Foo True)
True

请注意,您不再需要{-# LANGUAGE TypeFamilies #-} class HasBar a where type Bar a :: * bar :: a -> Bar a data Foo = Foo { barBool :: Bool } deriving (Show) instance HasBar Foo where type Bar Foo = Bool bar = barBool data Bazz = Bazz { barInt :: Int } deriving (Show) instance HasBar Bazz where type Bar Bazz = Int bar = barInt