类型

特性

强(strong)类型

静态(static)

可以通过自动推导(automatically inferred)得出

基本类型

Char
Bool
Int 常用 定长(fixed-width)整数 一般32/64位宽
Integer 不常用 不定长
Double 常用 浮点数
Float 不常用 计算慢
Prelude> :type 'a'
'a' :: Char

Prelude> 'a'            -- 自动推导
'a'

::符号
Prelude> 'a' :: Char    -- 显式签名
'a'

调用函数

Prelude> odd 3
True

Prelude> compare 2 3
LT
e.g. LT EQ GT

Prelude> (compare 2 3) == LT
True

Prelude> compare (sqrt 3) (sqrt 6)  -- 此处括号不可去
LT

复合数据类型:列表和元组

不同?

列表可以任意长,且只能包含类型相同的值。

元组的长度是固定的,但可以包含不同类型的值。

可递归性

[[Int]] 是一个包含 [Int] 类型值的列表,而 [Int] 又是一个包含 Int 类型值的列表即用 Int 类型替换了类型变量 a 。

列表[]及 head, tail, last ; take, drop

head 取第一个元素, tail 取除第一个外所有
last 取最后一个元素

Prelude> head [1, 2, 3, 4]
1

Prelude> head []
*** Exception: Prelude.head: empty list

Prelude> tail "list"
"ist"
ghci> last [1,2,3]
3
Prelude> take 2 [1, 2, 3, 4, 5]
[1,2]
Prelude> drop 2 [1, 2, 3, 4, 5]
[3,4,5]
Prelude> drop 4 [1, 2]
[]
Prelude> drop (-1) "foo"    --第一个参数小于或等于 0时, drop 函数返回整个输入列表
"foo"

元组 () 及 fst, snd

大小至少为2

元组的类型由它所包含元素的数量、位置和类型决定

Prelude> :type (True, "hello")
(True, "hello") :: (Bool, [Char])
Prelude> (4, ['a', 'm'], (16, True))
(4,"am",(16,True))
Prelude> :t ()
() :: ()
Prelude> fst (1, 'a')
1

Prelude> snd (1, 'a')
'a'

元组使用场景e.g.

  • 如果一个函数需要返回多个值,那么可以将这些值都包装到一个元组中,然后返回元组作为函数的值。

  • 当需要使用定长容器,但又没有必要使用自定义类型的时候,就可以使用元组来对值进行包装。

利用括号将表达式传给函数

a b c d 等于 (((a b) c) d)

纯度

带副作用的函数称为 不纯(impure)函数 ,而将不带副作用的函数称为 纯(pure)函数

副作用即函数中全局变量可修改如取决于某一时刻的值,本质上是函数的一种不可见的(invisible)输入或输出。Haskell 的函数在默认情况下都是无副作用的:函数的结果只取决于显式传入的参数。

不纯函数的类型签名都以 IO 开头,以下为检测e.g.:

Prelude> :type readFile
readFile :: FilePath -> IO String
-- 不纯函数的类型签名都以 IO 开头

加载.hs文件

ghci中通过:load

变量

一旦变量绑定了(也即是,关联起)某个表达式,那么这个变量的值就不会改变。如.hs文件中就不行,但ghci命令行里可以。

e.g. [Assign.hs]

条件求值

不同分支之间的类型必须相同。
e.g.:

[myDrop.hs]

-- file: ch02/myDrop.hs

myDrop :: Int -> [a] -> [a]
myDrop n xs = if n <= 0 || null xs    -- ## null用法
              then xs   -- 缩进不可省略
              else myDrop (n - 1) (tail xs) -- 缩进不可省略

-- 代换(substitution)和重写(rewriting)
-- 代换了变量n和xs
-- 重写了myDrop函数

-- ## 惰性求值
-- 也可写成一行 e.g.[myDropX.hs](ch02/myDrop.hs)

-- ## 条件求值
-- null xs not equals to xs == null
-- then 和 else 之后的表达式称为“分支”,
-- 不同分支之间的类型必须相同。
-- 而if不是分支。


-- e.g.
-- Prelude> if True then 1 else "foo"

-- <interactive>:2:14:
--     No instance for (Num [Char])
--         arising from the literal `1'
--     Possible fix: add an instance declaration for (Num [Char])
--     In the expression: 1
--     In the expression: if True then 1 else "foo"
--     In an equation for `it': it = if True then 1 else "foo"

->
可精简到一行 [myDropX.hs]

-- file: ch02/myDropX.hs
myDropX n xs = if n <= 0 || null xs then xs else myDropX (n - 1) (tail xs)

惰性求值

e.g. [isOdd.hs]

-- file: ch02/isOdd.hs
isOdd n = mod n 2 == 1

-- ghci> isOdd (1+2)
-- True

--in general:XXXXXX
-- first: (1+2)
-- second: mod 3 2
-- third: 1 == 1
-- True

--but in haskell:
--(1+2) 最后算!当真正有需要的时候再计算出 isOdd (1 + 2) 的值
--追踪未求值表达式的记录被称为块(chunk)

递归

注意惰性求值,递归 -> 终止递归 -> 递归返回
解释
e.g. [myDrop.hs]

多态

参数多态

Prelude> :type last
last :: [a] -> a
ghci> last [1,2,3]
3

如果将一个类型为 [Char] 的列表传给 last ,那么编译器就会用 Char 代换 last 函数类型签名中的所有 a ,从而得出一个类型为 [Char] -> Char 的 last 函数。诸如此类可以是Int,可以是任何类型。这种类型的多态被称为参数多态。

参数多态没有办法知道输入参数的实际类型

缺少

Haskell没有子类多态和强制多态(coercion polymorphism)。

多参数函数的类型

e.g.

Prelude> :type take
take :: Int -> [a] -> [a]