zoukankan      html  css  js  c++  java
  • 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

    1
    2
    3
    4
    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 类型:

    1
    2
    3
    4
    sealed trait Human

    final case class Worker(name: String, company: String) extends Human
    final 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

    例如:

    1
    2
    3
    sum :: [Int] -> Int
    sum [] = 0
    sum (x : xs) = x + sum xs

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

    1
    2
    3
    4
    5
    6
    7
    8
    desugar :: Statement -> DietStatement
    desugar (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

  • 相关阅读:
    149. Max Points on a Line(js)
    148. Sort List(js)
    147. Insertion Sort List(js)
    146. LRU Cache(js)
    145. Binary Tree Postorder Traversal(js)
    144. Binary Tree Preorder Traversal(js)
    143. Reorder List(js)
    142. Linked List Cycle II(js)
    141. Linked List Cycle(js)
    140. Word Break II(js)
  • 原文地址:https://www.cnblogs.com/dhcn/p/12685389.html
Copyright © 2011-2022 走看看