在this talk around the 1:20 mark中,Edward Kmett提到在Haskell中缺乏“类型类回溯”。考虑在列表上实现的“集合相等”(其中顺序和多重性被忽略)的问题:
equals :: [a] -> [a] -> Bool
根据类型类的性质,如果我们只有Eq a
,我就无法提供低效的O(n²)比较,但如果我们有Ord a
,则有效地比较O(n log n)中的列表。
我确实理解为什么这样的设施会有问题。与此同时,爱德华说,有些“你可以玩的技巧”提到了类型家庭。
因此我的问题是,实现相同效果的解决方法是什么:
此解决方法不一定需要使用类型类。
修改:有些人建议只提供equals
和efficientEquals
作为两种不同的方法。总的来说,我同意这是更多Haskell惯用法。但是,我不相信这总是可行的。例如,如果上面的equals方法本身是类型类的一部分,那该怎么办:
class SetLike s where
equals :: Eq a => s a -> s a -> Bool
假设此类已由其他人提供,因此我不能简单地向类型类添加新函数。我现在想要为[]
定义实例。我知道[]
可以始终提供equals
的实现,无论a
上有什么约束,但如果有关{{1}的更多信息,我无法告诉我的实例使用更高效的版本可用。
也许我可以将列表包装在newtype中并用一些其他类型信息标记它?
答案 0 :(得分:5)
在您的修改方案中,您可以使用GADT
作为Ord
约束的证明:
{-# LANGUAGE GADTs #-}
import Data.List
class SetLike s where
equals :: Eq a => s a -> s a -> Bool
data OrdList a where
OrdList :: Ord a=> [a] -> OrdList a
instance SetLike OrdList where
equals (OrdList a) (OrdList b) = sort a == sort b
为了好玩,这里使用的东西利用了我在评论中提到的OverlappingInstances
技巧。有很多方法可以做这种事情:
{-# LANGUAGE DefaultSignatures, OverlappingInstances, UndecidableInstances, MultiParamTypeClasses, FlexibleInstances #-}
import Data.List
class Equals a where
equals :: [a] -> [a] -> Bool
default equals :: Ord a=> [a] -> [a] -> Bool
equals as bs = sort as == sort bs
instance Equals Int
instance Equals Integer
instance Equals Char
-- etc.
instance (Eq a)=> Equals a where
equals = error "slow version"