我想了解Collection类型的以下两个定义之间的区别。
使用多参数类型类和函数依赖项(取自here);
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-}
class Eq e => Collection c e | c -> e where
insert :: c -> e -> c
member :: c -> e -> Bool
instance Eq a => Collection [a] a where
insert = flip (:)
member = flip elem
只有一个类型参数;
class Collection c where
insert :: Eq e => c e -> e -> c e
member :: Eq e => c e -> e -> Bool
instance Collection [] where
insert = flip (:)
member = flip elem
两者似乎都可以编译并正常工作。这两者之间是否有任何实际差异,或任何理由支持一种方法而不是另一种方法?
答案 0 :(得分:5)
一个简单的例子,Fundep版本仍然有效,但不是单参数版本,是a Set
container:
import qualified Data.Set as Set
instance (Ord e) => Collection (Set.Set e) e where
insert = Set.insert
member = flip Set.member
另一个类的实例不起作用,因为您需要Ord
约束。现在,您可以将定义更改为
class Collection c where
insert :: Ord e => c e -> e -> c e
member :: Ord e => c e -> e -> Bool
但是对于像列表这样只会令人讨厌的简单容器,您还希望在那里存储非ord类型。对于Hashmaps,您还需要另一个约束。 任何集合都需要这些全局和必需品。
答案 1 :(得分:4)
你的第二个变种的另一个用例是单形容器。例如,ByteString
可以被视为Char
的容器。
还应该注意的是,存在基于类型族的第三种替代方案,其是最灵活的类型。虽然在这个琐碎的案例中,它与基于fundeps的游戏没有多大区别。
{-# LANGUAGE TypeFamilies #-}
import qualified Data.ByteString.Char8 as B
class Collection c where
type Row c
insert :: c -> Row c -> c
member :: c -> Row c -> Bool
instance (Eq a) => Collection [a] where
type Row [a] = a
insert = flip (:)
member = flip elem
instance Collection B.ByteString where
type Row B.ByteString = Char
insert = flip B.cons
member = flip B.elem