我阅读了William Cook的“关于数据抽象,再访”,并重读了Ralf Laemmel的“表达引理”,试图理解如何在Haskell中应用前一篇论文的思想。所以,我试图理解你如何在没有指定类型的情况下在Haskell中实现例如集合联合函数?
答案 0 :(得分:14)
有多种方式,具体取决于您所使用的“抽象数据类型”版本。
混凝土但不透明的类型:我读了库克的可爱论文已经有一段时间了,但回头看看它我认为这与他所说的ADT最接近。在Haskell中执行此操作的标准方法是导出没有其构造函数的类型;这在Haskell中意味着什么:
抽象类型
除了使用从其模块
这与库克的论文有何关系:
表示独立性:从外部来看,表示无法访问。
检查多个表示:在ADT模块内部,可以自由检查表示。
独特的实现/模块:不同的模块可以提供不同的实现,但除了通常的方式之外,类型不能互操作。您无法使用Data.IntMap.null
查看Data.Map.Map Int a
是否为空。
这种技术广泛用于Haskell标准库,特别是对于需要维护某种不变量或以其他方式限制构造值的能力的数据类型。因此,在这种情况下,从文件中实现设置ADT的最佳方法是以下代码:
import qualified Data.Set as S
虽然这可能不像一个具有更具表现力的模块系统的语言那样强大的抽象手段。
存在量化和界面:Haskell实际上并没有exists
关键字,但术语“存在性”在各种情况下用于描述某些类型的多态类型。在每种情况下的一般想法是将值与在其上运行的函数集合组合,使得结果在值的类型中是多态的。考虑这个函数签名:
foo :: (a, a -> Bool) -> Bool
虽然它收到类型为a
的值,但因为a
是完全多态的,所以它可以对该值执行的唯一操作就是将函数应用于它。所以从某种意义上说,在这个函数中,元组的前半部分是“抽象数据类型”,而后半部分是用于处理该类型的“接口”。我们可以明确地提出这个想法,并使用存在性数据类型将其应用于单个函数之外:
data FooADT = forall a. FooADT a (a -> Bool)
foo :: FooADT -> Bool
现在,只要我们有FooADT
类型的值,我们都知道存在某种类型a
,以便我们可以应用FooADT
第一个问题的第二个论点。
同样的想法适用于具有类约束的多态类型;唯一的区别是对类型进行操作的函数是由类类型隐式提供的,而不是与值明确捆绑在一起。
现在,这对库克的论文意味着什么?
代表性独立仍然适用。
完全隔离:与以前不同,存在量化类型的知识永远丢失。除了它自己提供的界面外,没有什么可以检查表示。
任意实施:不仅实施不一定是唯一的,根本没有办法限制它们!任何可以提供相同界面的东西都可以包含在存在主体中,与其他值无法区分。
简而言之,这与库克对物体的描述非常相似。有关存在性ADT的更多信息,论文Unfolding Abstract Datatypes并不是一个糟糕的起点;但请记住,它所讨论的内容根本不是库克所谓的ADT。
还有一个简短的附录:我已经解决了上面描述存在类型抽象的所有问题,我想强调一下FooADT
类型的一些内容:因为所有你可以用它做的就是应用函数来获取一个Bool
结果,FooADT
和Bool
之间存在根本没有区别,除了前者模糊了您的代码并需要GHC扩展。在开始在Haskell代码中使用存在类型之前,我强烈鼓励reading this blog post。
答案 1 :(得分:1)
您可以要求提供比较功能,要么将类型作为Eq的实例。有关此技术的示例,请参阅nub
和nubBy
:
nub :: (Eq a) => [a] -> [a]
nubBy :: (a -> a -> Bool) -> [a] -> [a]