Haskell Algebraic datatypes

Data declarations review

data Bool = False | True

data [] a = [] | a : [a]
  • 关键字 data 声明数据类型
  • Bool 类型构造器,但是没有参数
  • 等号把数据构造器付给类型构造器
  • False True 是数据构造器但是没有接受任何参数,称为 nullary constructor
  • | 表示它是 sum type,可以分开逻辑它有什么类型的值
  • [] a 类型构造器,并且接受一个参数

当我们在讲数据声明的时候,是讲它整个类型的定义,类型可以有一个或多个构造器,构造器可以接受一个或多个参数,Bool 有两个构造器,两个构造器都不接受参数

Data and type constructors

在 Haskell 中有两种构造器,一种是类型构造器,一种是数据构造器, 我们可以在常量和构造器中做区别,类型和数据构造器如果没有接受参数,就是常量,他们只能保持那个固定的值,例如 Bool, Bool 是一个类型常量,True 和 False 也是常量,因为他们没有接受参数

data Trivial = Trivial'

data UnaryTypeCon a = UnaryValueCon a
  • Trivial 就像一个常量,它不接受任何参数
  • Trivial' 数据构造器也像一个常量值
  • UnaryTypeCon 是一个类型构造器,并且接受一个参数,它等待一个参数,然后应用它
  • UnaryValueCon 是一个数据构造器,接受一个参数它不像函数那样执行然后生成数据,这里它表现的想一个盒子,盒子里面有一个值

Type constructors and kinds

data [] a = [] | a : [a]

这里必须先应用一个类型到 a,然后才能获取到正确类型的数据,Kinds 是类型的类型,kinds 在 Haskell 中用 * 来表示。* -> * 就像一个函数,它等待应用一个类型,就可以生成 * 了。

Prelude> let f = not True
Prelude> :t f
f :: Bool
Prelude> 

我们可以用 :k 来查看类型构造器的签名

Prelude> :k Bool
Bool :: *
Prelude> 

Prelude> :k []
[] :: * -> *
Prelude> 

Data constructors and values

data PugType = PugData

data HuskyType a = HuskyData

data DogueDeBordeaux doge = 
     DogueDeBordeaux doge
  • PugType 是类型构造器,但是他没有接受参数,我们可以把他认为是类型常量
  • PugData 是类型 PugType 的数据构造器,它是一个常量值,当任何函数需要 PugType 类型,可以明确的知道他的值是 PugData
  • HuskyType 是一个类型构造器,他接受一个参数,有一个数据构造器
  • HuskyData 是 HuskyType 的一个数据构造器,这里的 HuskData 是一个常量值,就和 PugData 一样
  • DogueDeBordeaux 是一个类型构造器,它有一个类型参数
  • DogueDeBordeaux 是一个单独的数据构造器,它和类型构造器是同名的,但是他们不是同一个东西。

What's a type and what's data?

类型是静态的并在编译的时候解析,类型是在运行之前已经确定的,data 是在运行时工作的

Exer

data Price = 
  Price Integer deriving (Eq, Show)

data Manufacturer =
   Mini
 | Mazda
 | Tata
   deriving (Eq, Show)

data Airline =
   PapuAir
 | CatapultsRUs
 | TakeYourChancesUnited
   deriving (Eq, Show)

data Vehicle = Car Manufacturer Price
              | Plane Airline
                deriving (Eq, Show)

isCar :: Vehicle -> Bool
isCar (Car _ _) = True
isCar _         = False

isPlane :: Vehicle -> Bool
isPlane (Plane _) = True
isPlane _         = False

areCars :: [Vehicle] -> [Bool]
areCars = foldr f []
  where f = (\a b -> (if (isCar a) then True else False) : b)

areCars' :: [Vehicle] -> [Bool]
areCars' = map isCar

getManu :: Vehicle -> Manufacturer
getManu (Car m _) = m

Data constructor arities

Arity 指的是一个函数或者构造器接受几个参数,一个函数接受零个参数叫做 nullary

Data constructors 接受一个参数叫做 unary, data constructors 接受多于一个参数以上的参数叫做 products

-- nullary
data Example0 = 
  Example0
  deriving (Eq, Show)

  -- unary
  data Example1 = 
    Example1 Int
    deriving (Eq, Show)

-- product
data Example2 =
  Example Int String
  deriving (Eq, Show)

What makes these datatypes algebraic?

代数数据类型在 Haskell 中是有代数性质的,因为我们可以使用 sum product 这两种基本的操作来描述它,为什么可以用 sum 和 product 来描述它们,是因为它们可以用 cardinality(基数) 来总结它们

cardinality 是 datatype 的所有可能的情况,它可以是 0,也可以是无限种情况,知道有多少可能值出现在某种类型,可以帮助更加的理解你的程序。

Unary constructors

如果是 unary constructor 它的 cardinality 通常和它所含的 type 是一样的, 如下

data Goats = Goats Int deriving (Eq, Show)

Product types

Product types 类似于 C 语言的结构体,它就像用多个类型的值来表达一个单独的数据结构。元组就是一个 Product types

(,) :: a -> b -> (a, b)

Record syntax

Records 是 Haskell 提供给 product types 的一个额外的语法

data OperatingSystem =
      GnuPlusLinux
    | OpenBSDPlusNevermindJustBSDStill
    | Mac
    | Windows
    deriving (Eq, Show)

data ProgLang =
      Haskell
    | Agda
    | Idris
    | PureScript
    deriving (Eq, Show)

data Programmer = 
    Programmer {
        os :: OperatingSystem,
        lang :: ProgLang
    }
    deriving (Eq, Show)

allOperatingSystems :: [OperatingSystem]
allOperatingSystems =
    [
        GnuPlusLinux,
        OpenBSDPlusNevermindJustBSDStill,
        Mac,
        Windows
    ]

allLanguages :: [ProgLang]
allLanguages =
    [
        Haskell,
        Agda,
        Idris,
        PureScript
    ]

allProgrammers :: [Programmer]
allProgrammers = [ Programmer { os = o, lang = l }| o <- allOperatingSystems, l <- allLanguages ]

你可能感兴趣的:(Haskell Algebraic datatypes)