【发布时间】:2016-12-03 01:31:13
【问题描述】:
我玩过TypeFamilies、FunctionalDependencies 和MultiParamTypeClasses。在我看来,TypeFamilies 似乎没有在其他两个之上添加任何具体功能。 (但反之亦然)。但我知道类型族很受欢迎,所以我觉得我错过了一些东西:
类型之间的“开放”关系,例如转换函数,这在TypeFamilies 中似乎是不可能的。完成MultiParamTypeClasses:
class Convert a b where
convert :: a -> b
instance Convert Foo Bar where
convert = foo2Bar
instance Convert Foo Baz where
convert = foo2Baz
instance Convert Bar Baz where
convert = bar2Baz
类型之间的满射关系,例如一种类型安全的伪鸭类型机制,通常使用标准类型族来完成。完成MultiParamTypeClasses 和FunctionalDependencies:
class HasLength a b | a -> b where
getLength :: a -> b
instance HasLength [a] Int where
getLength = length
instance HasLength (Set a) Int where
getLength = S.size
instance HasLength Event DateDiff where
getLength = dateDiff (start event) (end event)
类型之间的双射关系,例如对于未装箱的容器,可以通过 TypeFamilies 使用数据系列来完成,但您必须为每个包含的类型声明新的数据类型,例如使用 newtype .无论是那个还是单射类型族,我认为在 GHC 8 之前不可用。使用 MultiParamTypeClasses 和 FunctionalDependencies 完成:
class Unboxed a b | a -> b, b -> a where
toList :: a -> [b]
fromList :: [b] -> a
instance Unboxed FooVector Foo where
toList = fooVector2List
fromList = list2FooVector
instance Unboxed BarVector Bar where
toList = barVector2List
fromList = list2BarVector
最后是两种类型和第三种类型之间的满射关系,例如python2或java风格的除法函数,可以用TypeFamilies完成,也可以用MultiParamTypeClasses。完成MultiParamTypeClasses 和FunctionalDependencies:
class Divide a b c | a b -> c where
divide :: a -> b -> c
instance Divide Int Int Int where
divide = div
instance Divide Int Double Double where
divide = (/) . fromIntegral
instance Divide Double Int Double where
divide = (. fromIntegral) . (/)
instance Divide Double Double Double where
divide = (/)
我还应该补充的另一件事是,FunctionalDependencies 和 MultiParamTypeClasses 似乎也更加简洁(无论如何对于上面的示例),因为您只需编写一次类型,而您不需要不必想出一个虚拟类型名称,然后您必须像使用 TypeFamilies 一样为每个实例键入它:
instance FooBar LongTypeName LongerTypeName where
FooBarResult LongTypeName LongerTypeName = LongestTypeName
fooBar = someFunction
对比:
instance FooBar LongTypeName LongerTypeName LongestTypeName where
fooBar = someFunction
因此,除非我另有说服力,否则我似乎真的不应该打扰TypeFamilies,而只使用FunctionalDependencies 和MultiParamTypeClasses。因为据我所知,这将使我的代码更简洁、更一致(少一个需要关心的扩展),并且还将给我更多的灵活性,例如开放类型关系或双射关系(可能后者是 GHC 的求解器8).
【问题讨论】:
-
类型族的性能通常比fundeps好得多——尤其是在实现类型函数时。
-
还有一个可读性问题。通过 FunDeps 表示的
type F a = G (H (G a) (G a))的等价物需要几个约束,涉及一些辅助类型变量。当我读到这样的约束时,我发现自己试图以一种函数的形式来表达它们。也许这是因为我更习惯于阅读函数式代码而不是 prolog。 -
@ErikR 为什么会这样?这似乎非常奇怪,因为两个选项都可行的示例在类型分辨率方面看起来完全一样,而对我来说不是。这只是
FunctionalDependencies或MultiParamTypeClasses或其交互的当前实现中的一个缺陷吗?或者它是更基本的东西?我真的希望是前者,因为出于性能原因,不必在同一件事的一半时间使用不同且看似更冗长的语法会很好。 -
@chi 你介意再深入一点你的意思吗?我有点困惑,因为通常我看到
type F a或type F a :: *没有=或者我看到type F G = ...在F之后立即有一个具体类型,为什么在F之后有一个类型变量和一个=。我对这些东西有点陌生。 -
@chi 如果您正在谈论在
TypeFamilies之外使用type,这似乎是使您的示例正常工作的唯一方法,那么我看不出TypeFamilies之间有什么不同和FunctionalDependencies,如果有任何区别,TypeSynonymInstances会缓解吗?
标签: haskell typeclass functional-dependencies type-families