我想使用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
的替代方案只允许一个实例并允许这种伪鸭型的东西工作吗?
答案 0 :(得分:4)
问题在于,它不仅要查找看到的内容,还能查找知道的内容 - 并且您有可能创建一个{ {1}}以及它抱怨
您可以使用FunctionalDependencies
或TypeFamilies
第一个扩展可能就是这里的方式(您不必更改大部分代码)。您基本上可以告诉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