1 值、函數、與定義

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?}