《Haskell趣学指南》笔记之类型(type)

2,085 阅读4分钟

系列文章


这本书居然没有告诉我怎么加注释,我自己搜了一下

  • 单行注释以 -- 开头
  • 多行注释用 {- whatever -} 括起来

继续看书

显式类型声明

  • :t <exp> 可以得到对应的类型
  • 类型是大写字母开头的
  • 给单参数函数声明类型,语法是 :: ParamType -> ReturnType
    removeNonUppercase :: [Char] -> [Char] 
    removeNonUppercase st = [ c | c <- st, c ` elem` ['A'..' Z']] 
    
  • 给多参数函数声明类型,语法是 :: Param1Type -> Param2Type ->ReturnType
    addThree :: Int -> Int -> Int -> Int 
    addThree x y z = x + y + z
    

基本类型

  • Int -- 有界
  • Integer -- 无界,可以存超大整数,效率比 Int 低
  • Float / Double / Bool / Char / String就是[Char]
  • tuple 的类型由其内部的元素类型决定,空 tuple 是一个单独的类型,只有一个值 ()

类型变量(类似于泛型)

  • head 的类型是 head :: [a] -> a
    • 注意这里的 a 是小写,不是大写,因为 a 是类型变量,不是类型
    • 一般类型变量只使用一个字母,但是用多个字母也不报错
  • 多态函数:使用了类型变量的函数。

类型类 typeclass

  • 类似于接口,但是它是用来约束类型 type 的。
  • 一个 type 可以是多个 typeclass 的实例,一个 typeclass 可以有多个 type 实例。
  • Eq 是最常见的类型类,Haskell 中标准类型都是 Eq 的实例,Eq 要求它的实例必须实现 ==/= 两个函数
  • 我们可以看看 == 运算符(也是函数)的类型
    ghci> :t (==) 
    (==) :: (Eq a) => a -> a -> Bool
    
  • 看上面代码,类型声明 a->a->Bool 前面有个 =>,再前面有个 (Eq a)
    • 这个 (Eq a) 叫做类型约束
    • 它约束 a 必须是 Eq 的实例,这样就保证了 a 必须实现 == 函数,以提供给全局的 == 函数调用
  • Ord 类型类要求实例必须实现 < / <= / > / >= 等比较操作
  • Ord 的有三种值:GT / LT / EQ,可以用 compare 得到
    ghci> compare 1 2
    LT
    
  • Show 类型类要求实例可以表示为字符串
  • show 函数可以把任意 Show 实例变为字符串
  • Read 类型类跟 Show 相反,它把非字符串的东西转为字符串
  • read 函数是 show 的相反操作
    ghci> show True 
    "True"
    ghci> (read "True") || False 
    True 
    
  • 注意直接 read "True" 会报错,因为 GHCi 需要根据后续的操作来确定你要把字符串转成什么类型
  • 类型注解:可以使用类型注解来告诉 GHCi 这玩意是什么类型。read "True" :: Bool 就不会报错,看起来很像 TypeScript 的 as 关键字
  • Enum 类型类要求实例有 successer 后继和 predecesor 前趋两个操作。可以使用 succ 函数和 pred 函数得到一个实例的后继和前驱。
  • Enum 的例子
    • ['a' .. 'e'] -- "abcde"
    • [LT..GT] -- [LT, EQ, GT]
  • Bounded 类型类要求实例有上限和下限
  • 用 maxBound 和 minBound 可以获取实例的上下限
  • 这两个函数的类型是 (Bounded a)=> a,这叫多态常量(跟多态函数对应)。
  • maxBound :: (Bool, Int, Char) 会得到每个实例的上限组成的 tuple,即 (True, 2147483647, \1114111)
  • Num 类型类要求实例具有数的特征。
  • :t 20 的结果是 (Num t)=> t
    • 这很奇怪, 20 的类型居然不是 Int 或者 Integer 或者 Float 之类的,而是这些类型对应的 typeclass 对应的多态常量,有点烧脑。
  • 所有的数都是多态常量,可以具有任何 Num 的实例的特征,也就是说 20 既可以是 Int / Integer 也可以是 Float / Double 等。
  • Floating 类型要求实例具有浮点数的特征,如 Float 和 Double 都是 Floating 的实例。
  • sin :: Floating a => a -> a
  • Integeral 类型要求实例具有整数的特征,如 Int 和 Integer。
  • fromIntegral :: (Num b, Integral a) => a -> b
  • length [1,2,3] + 3.2 会报错,解决办法是 fromIntegral (length [12,3]) + 3.2

类型类概览

  • Haskell 中所有标准 type 都是 Eq 的实例,除了输入输出相关类型和函数
  • 目前我们遇到的所有 type 都是 Ord 的实例,除了函数
  • 目前我们遇到的所有 type 都是 Show 的实例,除了函数
  • 目前我们遇到的所有 type 都是 Read 的实例,除了函数
  • Enum 类型类包含的 type 有 () / Bool / Char / Ordering / Int / Integer / Float / Double
  • Bounded 包含的 type 有 Int / Char / Bool 等
  • Num 包含 Int / Integer / Float / Double,只有已经属于 Show 和 Eq 的类型实例,才能成为 Num 的实例,这是一个先决条件( prerequisite)
  • Floating 包含 Float / Double
  • Integeral 包含 Int / Integer