1.5 $\lambda$ 算式
雖說函數是一級市民,在本書之中,目前為止,仍有一項功能是其他型別擁有、函數卻還沒有的:寫出一個未命名的值的能力。
整數、真假值都能不經命名、直接寫在算式中,例如,
我們可寫 smaller (square 3) 4, 而不需要先定義好
num1, num2 :: Int
num1 = 3
num2 = 4
才能說 smaller (square num1) num2. 但使用函數時,似乎非得
先給個名字,才能使用它:
square :: Int -> Int
square x = ...
quad = twice square
如果為了某些原因(例如,如果在我們的程式中 square 只會被用到一次),我們不想給 square 一個名字,我們能不能直接把它寫出來呢?
$\lambda$ 算式便是允許我們這麼做的語法。
以直觀的方式解釋,\x -> e 便是一個函數,其中 x 是參數,
e 是函數本體。
例如,(\x -> x * x) 是一個函數,計算其輸入(x)的平方。
如果我們不想給 square 一個名字, 我們可將 quad 定義為:
quad = twice (\x -> x * x)
寫成 $\lambda$ 算式的函數也可直接作用在參數上,例如 (\x -> e1) e2。
這個式子歸約的結果通常表示為e1[e2/x],
意思是「將 e1 之中的 x 代換為 e2」。
例如,算式 (\x -> x * x) (3+4) 可歸約為 (3+4) * (3+4).
例 1.10
以下是一些 $\lambda$ 算式的例子:
函數
(\x -> 1 + x)把輸入遞增 --- 和(1+)相同。 其實,把(1+)的語法糖去掉後,得到的就是這個 $\lambda$ 算式。(\x -> \y -> x + 2 * x * y)是一個傳回 $\lambda$ 算式的函數。(\x -> \y -> x + 2 * x * y) (3+4)可歸約為(\y -> (3+4) + 2 * (3+4) * y)。注意\x不見了, 函數本體中的x被代換成3+4,\y -> ..則仍留著。(\x -> \y -> x + 2 * x * y) (3+4) 5可歸約為(3+4) + 2 * (3+4) * 5.
由於傳回函數的函數是常見的,Haskell (如同 $\lambda$-calculus) 提供較短的語法。上述例子中的函數也可簡寫成:
(\x y -> x + 2 * x * y).函數也可以當參數。例如,
(\x -> x 3 3) (+)可歸約為(+) 3 3, 或3 + 3.以下是
(\f x -> f x x) (\y -> 2 * y) 3的求值過程:
(\f x -> f x x) (\y z -> 2 * y + z) 3
= (\x -> (\y -> 2 * y + z) x x) 3
= (\y -> 2 * y + z) 3 3
= 2 * 3 + 3
= 9
在
\x -> e之中,x是範圍限於e的區域識別字。 因此:
(\f x -> x + f x) (\x -> x + x) 3
= (\x -> x + (\x -> x + x) x) 3
= 3 + (\x -> x + x) 3
= 3 + 3 + 3
= 9
有了 $\lambda$ 算式後,函數 smaller 又有另一種寫法:
smaller :: Int -> Int -> Int
smaller = \x y -> if x <= y then x else y
事實上,$\lambda$ 算式可視為更基礎的機制 --- 目前為止我們所介紹的種種語法結構都僅是 $\lambda$ 算式的語法糖,都可展開、轉譯為 $\lambda$ 算式。 Haskell 的 $\lambda$ 算式源於一套稱為 $\lambda$ 演算 ($\lambda$ calculus)的形式語言 --- 這是一個為了研究計算本質而發展出的理論,也是函數語言的理論核心。我們將在爾後的章節中做更詳盡的介紹。\todo{which?}