haskell – 类型族可以做什么,多param类型类和函数依赖不能
我玩过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样式除法函数,它们也可以通过使用MultiParamTypeClasses与TypeFamilies完成.使用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 VS: instance FooBar LongTypeName LongerTypeName LongestTypeName where fooBar = someFunction 因此,除非我确信,否则我真的应该不打扰TypeFamilies并仅使用FunctionalDependencies和MultiParamTypeClasses.因为据我所知,它将使我的代码更简洁,更一致(更少关注的扩展),并且还会给我更多的灵活性,例如开放式关系或双向关系(可能后者是GHC的解决方案) 8). 解决方法
下面是一个示例,与具有FunctionalDependencies的MultiParamClasses相比,TypeFamilies真正发挥作用.事实上,我挑战你提出一个等效的MultiParamClasses解决方案,即使是使用FlexibleInstances,OverlappingInstance等的解决方案.
考虑类型级别替换的问题(我在QData.hs中遇到过Quipper的特定变体).基本上你想要做的是以递归方式将一种类型替换为另一种类型.例如,我希望能够 >在[Int]字符串中用Int替换Bool并获取[Bool]字符串, 总而言之,我想要通常的类型级别替换概念.对于一个封闭类型的家庭,我可以为任何类型执行此操作(虽然我需要为每个更高级的类型构造函数添加额外的行 – 我停在* – > * – > * – > * – > *). {-# LANGUAGE TypeFamilies #-} -- Subsitute type `x` for type `y` in type `a` type family Substitute x y a where Substitute x y x = y Substitute x y (k a b c d) = k (Substitute x y a) (Substitute x y b) (Substitute x y c) (Substitute x y d) Substitute x y (k a b c) = k (Substitute x y a) (Substitute x y b) (Substitute x y c) Substitute x y (k a b) = k (Substitute x y a) (Substitute x y b) Substitute x y (k a) = k (Substitute x y a) Substitute x y a = a 尝试ghci我得到了所需的输出: > :t undefined :: Substitute Int Bool (Either [Int] String) undefined :: Either [Bool] [Char] > :t undefined :: Substitute [Int] Bool (Either [Int] String) undefined :: Either Bool [Char] > :t undefined :: Substitute [Int] [Bool] (Either [Int] String) undefined :: Either [Bool] [Char] 话虽如此,也许你应该问自己为什么我使用MultiParamClasses而不是TypeFamilies.在上面给出的示例中,除转换之外的所有示例都转换为类型族(尽管每个实例需要额外的行用于类型声明). 再说一次,对于Convert,我不相信定义这样的东西是个好主意. Convert的自然扩展将是诸如的实例 instance (Convert a b,Convert b c) => Convert a c where convert = convert . convert instance Convert a a where convert = id 这对GHC来说是无法解决的,因为它们写得很优雅…… 为了清楚起见,我并不是说没有使用MultiParamClasses,只是在可能的情况下你应该使用TypeFamilies – 它们让你考虑类型级函数而不仅仅是关系. This old HaskellWiki page does an OK job of comparing the two. 编辑 我从08002年8月偶然发现了一些对比和历史
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |