我正在阅读纯功能数据结构的第2章,其中讨论了作为二叉搜索树实现的无序集。代码以ML编写,最终显示signature ORDERED
和functor UnbalancedSet(Element: ORDERED): SET
。来自更多的C ++背景,这对我来说很有意义;自定义比较函数对象构成了类型的一部分,可以在构造时传入,这看起来非常类似于ML函数的处理方式。
当谈到Haskell时,似乎行为仅取决于Ord
实例,所以如果我想要一个其顺序颠倒的集合,似乎我必须使用{{ 1}}实例,例如
newtype
然后我可以在一个集合中使用:
newtype ReverseInt = ReverseInt Int deriving (Eq, Show)
instance Ord ReverseInt where
compare (ReverseInt a) (ReverseInt b)
| a == b = EQ
| a < b = GT
| a > b = LT
有没有更好的方法来做这种事情,而不是使用let x = Set.fromList $ map ReverseInt [1..5]
来创建不同的newtype
实例?
答案 0 :(得分:9)
不,这真的是要走的路。是的,有一个newtype
有时很烦人,但你会得到一些好处:
Set a
而您知道a
时,您会立即知道它使用的比较类型(与纯度使代码更具可读性的方式相同,因为您不必跟踪执行)。您不必知道Set a
来自何处。coerce
一次通过多种新类型。例如,我可以使用xs = [1,2,3] :: Int
将ys = [ReverseInt 1, ReverseInt 2, ReverseInt 3] :: [ReverseInt]
转换为ys = coerce xs :: [ReverseInt]
。不幸的是,Set
的情况并非如此(并且它不应该 - 你需要强制函数是单调的,以免搞砸数据结构不变量,并且没有还有一种在类型系统中表达的方法。)newtype
最终会比你期望的更具有组合性。例如,您创建的ReverseInt
类型已经存在于一种形式,该形式可以推断为使用Ord
约束来反转任何类型:它被称为Down
。要明确,您可以使用Down Int
代替ReversedInt
,然后获得免费写的实例!当然,如果您对此仍然非常强烈,那么没有什么能阻止您编写您的Set
版本,该版本必须具有它所使用的比较函数的字段。像
data Set a = Set { comparisionKey :: a -> a -> Ordering
, ...
}
然后,每次制作Set
时,都必须传入比较密钥。