Algebraic Data Type 及其在 Haskell 和 Scala 中的表现

http://songkun.me/2018/07/12/2018-07-12-adt-in-haskell-and-scala/

函数式编程接触久了以后,我们会发现很多 FP 语言(这里指静态 FP 语言)具有不少类似的语言特性,这非常自然,因为语言特性就那么多,好用、实用的特性更少,这一方面造成了语言之间的同质化,另一方面也减轻了我们语言切换的成本,算是有利也有弊吧。

常见的静态函数式语言有 Haskell、Standard ML、OCaml、Scala 等,它们之间非常类似,共有的特性有:

  • first-class function(匿名函数、高阶函数、闭包、柯里化、部分应用函数…)
  • algebraic data type
  • 模式匹配
  • 类型推断

各个语言也有自己相对独特的特性,比如 Haskell 的 typeclass,Standard ML 和 OCaml 的模块,Scala 的私货就更多了,为了兼容 OO 和 FP 做了很多设计上的妥协,也引入了很多概念。

函数式编程语言也有动态、静态之分,上面列举的都是静态语言,动态语言则以 Lisp 为代表,包括:Scheme、Racket、Clojure、Erlang、Elixir 等,由于动态类型的限制,它们没有 algebraic data type、类型推断等类型系统相关的特性。

说了这么多,只是想告诉大家,ADT(algebraic data type)真真是 FP 语言中非常常见的概念,另外 ADT 与模式匹配可谓焦不离孟,孟不离焦,搭配起来威力强大。

什么是 ADT ?

说了这么多,到底什么是 ADT 呢?

不要着急,我们知道编程语言会内置一些基本类型,例如 intString 等,基本类型只能表示非常简单的值,无法对复杂的现实世界建模,因此编程语言还会提供将基本类型 组合 成复杂类型机制,这些组合出来的类型被称为组合类型,维基百科对组合类型的定义为:

In computer science, a composite data type or compound data type is any data type which can be constructed in a program using the programming language’s primitive data types and other composite types.

注:编程语言中的两种类型:

  • 基本类型
  • 组合类型

不同编程语言提供了不同的构建组合类型的机制,例如面向对象中的 class、struct 等,ADT 正是函数式编程语言构建组合类型的方式。

为什么叫 algebraic data type 呢,难不成还跟代数有关系???

Yes,还真和代数有关,根据 Haskell wiki:

“Algebraic” refers to the property that an Algebraic Data Type is created by “algebraic” operations. The “algebra” here is “sums” and “products”:

  • “sum” is alternation (A | B, meaning A or B but not both)
  • “product” is combination (A B, meaning A and B together)

ADT 是通过 代数运算 构造的,常见的两种构造方式为:

  • sum,即 a or b
  • product,即 a and b

例如 Tuple 是由 product(a and b) 操作产生的,而 Option Either Try 是由 sum(a or b) 操作产生的。

product 类型

product 类型是最常见的组合类型,class 在某种程度上也是 product 类型。

Scala 可以用 case 类构建 product 类型:

1
case class Student(name: String, age: Int)

也可以用 trait

1234
trait Student {  def name: String  def age: Int}

而 Haskell 则用 record 语法构建:

1
data Student = Student { name:: String, age:: Int }

product 类型非常常见,常见到我们几乎已经忽略掉它了,俗称灯下黑。。。

sum 类型

ADT 的强大之处更多体现在 sum 类型上,面向对象没有与之对应的特性(C/C++ 的 struct、Java 的 Enum 有点形似,但功能差太远)

Scala 通过 sealed trait + case 类构建 sum 类型:

1234
sealed trait Human

final case class Worker(name: String, company: String) extends Humanfinal case class Student(name: String, school: String) extends Human

而 Haskell 则更加简洁:

1
data Human = Worker String String | Student String String

两者语法有差异,但实际上非常神似:

  • Haskell 中的 Worker 和 Student 都是 value constructor,则 Scala 中的 Worker 和 Student 也是 constuctor;
  • Haskell 中定义的类型是 Human,而 Scala 虽然定义了 3 个不同类,但 Worker 和 Student 是 Human 的子类,因此可以认为仅定义了 Human 一个类型;
  • Scala 通过 sealed 帮助编译器实现穷尽分析,而 Haskell 无需做任何额外工作,默认即获得穷尽分析的能力;

模式匹配

说到这里,大家肯定会想,ADT 也不过如此嘛,哪有什么神奇的地方?

好吧,ADT 本身的确没什么特别,ADT 的威力需要借助模式匹配才能释放出来,ADT + 模式匹配,这可能是我见过的最具有表现力、最实用、最强的语言特性了,非常赞!

模式匹配其实就做了两件事:

  1. 匹配类型
  2. 解构 ADT

例如:

123
sum :: [Int] -> Intsum []       = 0sum (x : xs) = x + sum xs

借助 ADT 和模式匹配,可以非常容易做解析:

12345678
desugar :: Statement -> DietStatementdesugar (Incr name)      = (DAssign name (Op (Var name) Plus (Val 1)))desugar (For s1 e s2 s3) = (DSequence (desugar s1) (DWhile e (DSequence (desugar s3) (desugar s2))))desugar (Assign name e)  = (DAssign name e)desugar (If e s1 s2)     = (DIf e (desugar s1) (desugar s2))desugar (While e s)      = (DWhile e (desugar s))desugar (Sequence s1 s2) = (DSequence (desugar s1) (desugar s2))desugar Skip             = DSkip

更多例子,可以参考 A tiny interpreter in Haskell

原文地址:https://www.cnblogs.com/dhcn/p/12685389.html

时间: 2024-10-22 05:11:04

Algebraic Data Type 及其在 Haskell 和 Scala 中的表现的相关文章

Scala 深入浅出实战经典 第55讲:Scala中Infix Type实战详解

王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt6 腾讯微云:http://url.cn/TnGbdC 360云盘:http://yunpan.cn/cQ4c2UALDjSKy 访问密码 45e2土豆:http://www.tudou.com/programs/view/9JKSqMiQuBE/优酷:http://v.youku.com/v_show/id

PHP 笔记一(systax/variables/echo/print/Data Type)

PHP stands for "Hypertext Preprocessor" ,it is a server scripting language. What Can PHP Do? PHP can generate dynamic page content PHP can create, open, read, write, delete, and close files on the server PHP can collect form data PHP can send an

Linux C double linked for any data type

/************************************************************************** * Linux C double linked for any data type * 声明: * 提供一种双链接口,可以保存保存任何类型的数据. * * 2015-12-25 晴 深圳 南山平山村 曾剑锋 **********************************************************************

诡异错误二:TypeError: data type not understood

如何使用Python产生一个数组,数组的长度为1024,数组的元素全为0? 很简单啊, 使用zeros(1024) 即可实现! 如何产生一个2×1024的全0矩阵呢?是否是zeros(2,1024) ? 若是上述这种写法就会出现 TypeError: data type not understood  这种错误: 正确的写法是 zeros((2,1024)),python的二维数据表示要用二层括号来进行表示. 三维数据是否使用三层括号?试一试,果然可以正确输出!试猜一猜, 下述三层括号中的数字分

【Agile Pair Coding】Data Type Mapping

介绍 今天下午用了1个小时左右,和同事Agile Pair Coding敏捷开发了一把,感觉挺爽的. Agile Pair Coding给我们带来的直接好处是:相互不浪费时间,高效:idea很快达成共识,不纠结于无谓的讨论:idea立马coding,不沉迷于头脑风暴:代码更严谨:重构概率大:加深基情:相互学习,相互欣赏,相互指正:避免无知,避免自我感觉良好...... 代码主要实现:从所有类型文件中,得到所有NE类型下的所有Object类型下的所有属性的数据类型. 当然,本文只是一个短时间内的D

JAVA 1.2(原生数据类型 Primitive Data Type)

1. Java的数据类型分为2类 >> 原生数据类型(primitive data type) >> 引用数据类型(reference data type) 3. 常量和变量 常量: 所谓常量,就是值不会变化的量: 变量,就是值可以变化的量. 4. 如何定义和使用变量? int a; //变量的申明 a = 10; // 变量的初始化 int b = 20; // 变量的申明和初始化 注意事项: 如果没有初始化会出以下结果: 在Java中的== 和= 的区别: =  代表的是赋值操

Value too large for defined data type的解决方法之一

折腾了两三天,尝试了网上各种办法最后终于把这个给解决了. 进入正题 之前用的是arm-2009的编译链,在不是共享文件夹下编译的话一切都正常,可以生成想要的目标文件,一旦放入/mnt/hgfs/xxx/(xxx表示自己创建的共享文件夹名称)然后进行make指令后就会出现Value too large for defined data type无奈之下改安装arm-2012的交叉工具链,得以解决.可以保留之前安装的arm-2009不影响后者工作,但要记得进入cd ~vi .bashrc中的末行添加

7.1The ISO SQL Data Type

In this section we introduce the data types defined in the SQL standard. We start by defining what constitutes a vailed identifer in SQL. 7.1.1  SQL Identifiers SQL identifiers are identify objects in the database, such as table names, views names, a

ADT (Abstract Data Type)

抽象数据类型 ADT (Abstract Data Type) 是一个实现包括储存数据元素的存储结构以及实现基本操作的算法.在这个数据抽象思想中,数据类型的定义和它的实现是分开的,这在软件设计中是一个重要的概念.这使得只研究和使用它的结构而不用考虑它的实现细节成为可能. ADT包括数据数据元素,数据关系以及相关的操作. 即ADT { 数据对象:(数据元素集合) 数据关系:(数据关系二元组结合) 基本操作:(操作函数的罗列) }