![]() |
1
62
ADT(在本文中,它不是抽象数据类型,这甚至是另一个概念,而是代数数据类型)和类型类是完全不同的概念,可以解决不同的问题。
ADT,缩写如下,是一种数据类型。需要ADT来构建数据。我认为,Scala中最接近的匹配是case类和密封特性的组合。这是在Haskell中构建复杂数据结构的主要方法。我认为ADT最著名的例子是
这种类型在标准Scala库中有一个直接等价物,称为
事实并非如此
基本上,ADT是几个命名元组(0-元,如
考虑以下数据类型:
这是一个简单的二叉树。这两个定义基本如下:“二叉树要么是
这个概念非常简单,但也非常强大。
正如您所注意到的,ADT是
关闭
,即在定义了类型之后,不能添加更多的命名元组。在Haskell中,这是在语法上强制执行的,而在Scala中,这通过
这些类型被称为代数是有原因的。命名元组可以被认为是乘积(在数学意义上),也可以被认为这些元组的“组合”是求和(在数学含义上),这种考虑具有深刻的理论意义。例如,前面提到的二叉树类型可以这样写:
但我认为这超出了这个问题的范围。如果你想了解更多,我可以搜索一些链接。
另一方面,类型类是定义多态行为的一种方式。大致类型类是特定类型提供的契约。例如,你知道你的价值
通常将类型类与Java接口进行比较,例如:
通过这种比较,类型类的实例与接口的实现相匹配:
接口和类型类之间有非常重要的区别。首先,您可以编写自定义类型类并 任何 键入它的实例:
但是你不能用接口做这样的事情,也就是说,你不能让一个现有的类实现你的接口。正如您也注意到的,这个特性意味着类型类 打开 . 这种为现有类型添加类型类实例的能力是解决问题的一种方法 expression problem Java语言没有解决它的方法,但Haskell、Scala或Clojure有。
类型类和接口之间的另一个区别是,接口仅在第一个参数上是多态的,即在隐式参数上
使用接口是不可能做到这一点的。 类型类可以在Scala中使用隐式参数进行模拟。这种模式非常有用,以至于在最近的Scala版本中甚至有一种特殊的语法可以简化它的使用。以下是操作方法:
正如您所看到的,类型类是一种接口,但功能更强大。您甚至可以选择类型类的不同实现,而使用它们的代码保持不变。然而,这种力量是以样板和额外实体为代价的。
请注意,可以编写与上述Scala程序等效的Haskell程序,但需要编写多个模块或
BTW,Clojure,一种在JVM上工作的Lisp方言,已经 协议 ,将接口和类型类组合在一起。协议是在单个第一个参数上调度的,但您可以为任何现有类型实现协议。 |
![]() |
2
8
你的问题实际上涉及 三 不同的概念: 类型类、抽象数据类型和 代数的 数据类型。 令人困惑的是,“抽象”和“代数”数据类型都可以 简称“ADT”;在Haskell上下文中,ADT几乎总是意味着 “代数”。 让我们定义这三个术语。 代数数据类型(ADT)是一种可以通过组合 更简单的类型。这里的核心思想是“构造函数”,它是一个 定义值的符号。将其视为 Java风格的枚举,但它也可以接受参数。最简单的 代数数据类型只有一个没有参数的构造函数:
这种类型的值只有一个:
第一种方法是给出我们的构造函数参数。例如,我们可以
现在
构建更复杂类型的另一种方法是有多个构造函数可供选择。例如,我们可以同时拥有
现在是类型的值
它的工作方式正是你所期望的。真正有趣的类型可以使用这两种方法来组合它们自己。作为一个相当做作的例子,想象一下形状:
形状可以是由其两个角定义的矩形,也可以是作为中心和半径的圆。(我们只是定义
使用代数数据类型可以做的一件重要事情是
图案匹配
这基本上意味着能够在ADT的替代方案上进行分支。作为一个非常简单的例子,您可以在上进行模式匹配,而不是使用if表达式
如果您的构造函数有参数,您也可以通过模式匹配来访问这些值。使用
这个
这只是代数数据类型的基本概述:事实证明,还有更多的乐趣。你可能想看看 relevant chapter 在里面 让你成为哈斯克尔 (简称LYAH)以获取更多阅读。
现在,怎么样
摘要
数据类型?这指的是一个不同的概念。抽象数据类型是实现不公开的类型:您不知道该类型的值实际是什么样子的。你唯一能做的就是应用从它的模块导出的函数。你不能在上面进行模式匹配,也不能自己构建新的价值观。实践中的一个很好的例子是
代数类型和抽象类型在某种程度上是正交的概念;很不幸的是,他们的名字很容易让人把其中一个误认为另一个。 拼图的最后一块是 类型类 。与代数和抽象数据类型不同,类型类本身不是类型。更确切地说,将类型类视为 设置 类型。特别是,类型类是实现某些函数的所有类型的集合。
最简单的例子是
类型类是由类型需要实现的函数来定义的。例如,
现在添加一个新类型,如
在此之后,
还有更多的类型类;你可以在 same LYAH chapter . 从技术上讲,还有另一个值叫做(bottom),但我们现在将忽略它。你可以稍后了解。
实际上,
|
![]() |
3
6
类型类和ADT之间的区别是:
例如,考虑
类型是静态的,在程序的整个生命周期内不能更改,因此,当您使用类型类时,您使用的方法在编译时会根据调用站点上推断的类型进行静态选择。所以在这个例子中,我知道我正在使用
ADT允许您动态地更改函数的行为。例如,我可以定义:
现在,如果我打电话
…我不知道是哪个分支
|