☰ OCaml 語言
第 12 章 語言擴展
23 綁定運算子
(於 4.08.0 版本引入)
綁定運算子提供了語法糖,可以將函式庫函數以(一種變體的)熟悉的標準關鍵字語法公開。目前支援的「綁定運算子」有 let<op> 和 and<op> ,其中 <op> 是一個運算子符號,例如 and+$ 。
引入綁定運算子是為了提供方便的語法來處理單子 (monads) 和應用函子 (applicative functors);對於這些,我們建議分別使用運算子 * 和 + 的慣例。它們可能被用於其他目的,但應記住,引入的每個新的不熟悉的表示法都會使非專業人士更難以理解程式。我們預期隨著時間的推移,將會在其他運算子系列上開發新的慣例。
23.1 範例
使用者可以定義let 運算子
let ( let * ) o f = match o with | None -> None | Some x -> f x let return x = Some x
val ( let * ) : 'a option -> ('a -> 'b option) -> 'b option = <fun > val return : 'a -> 'a option = <fun >
然後使用這種方便的語法來應用它們
let find_and_sum tbl k1 k2 = let * x1 = Hashtbl.find_opt tbl k1 in let * x2 = Hashtbl.find_opt tbl k2 in return (x1 + x2)
val find_and_sum : ('a, int) Hashtbl.t -> 'a -> 'a -> int option = <fun >
這等同於這個展開的形式
let find_and_sum tbl k1 k2 = ( let * ) (Hashtbl.find_opt tbl k1) (fun x1 -> ( let * ) (Hashtbl.find_opt tbl k2) (fun x2 -> return (x1 + x2)))
val find_and_sum : ('a, int) Hashtbl.t -> 'a -> 'a -> int option = <fun >
使用者也可以定義 and 運算子
module ZipSeq = struct type 'a t = 'a Seq.t open Seq let rec return x = fun () -> Cons(x, return x) let rec prod a b = fun () -> match a (), b () with | Nil, _ | _, Nil -> Nil | Cons(x, a), Cons(y, b) -> Cons((x, y), prod a b) let ( let + ) f s = map s f let ( and + ) a b = prod a b end
module ZipSeq : sig type 'a t = 'a Seq.t val return : 'a -> 'a Seq.t val prod : 'a Seq.t -> 'b Seq.t -> ('a * 'b) Seq.t val ( let + ) : 'a Seq.t -> ('a -> 'b) -> 'b Seq.t val ( and + ) : 'a Seq.t -> 'b Seq.t -> ('a * 'b) Seq.t end
以支援以下語法
open ZipSeq let sum3 z1 z2 z3 = let + x1 = z1 and + x2 = z2 and + x3 = z3 in x1 + x2 + x3
val sum3 : int Seq.t -> int Seq.t -> int Seq.t -> int Seq.t = <fun >
這等同於這個展開的形式
open ZipSeq let sum3 z1 z2 z3 = ( let + ) (( and + ) (( and + ) z1 z2) z3) (fun ((x1, x2), x3) -> x1 + x2 + x3)
val sum3 : int Seq.t -> int Seq.t -> int Seq.t -> int Seq.t = <fun >
23.2 慣例
應用函子應提供一個實現以下介面的模組
module type Applicative_syntax = sig type 'a t val ( let + ) : 'a t -> ('a -> 'b) -> 'b t val ( and + ): 'a t -> 'b t -> ('a * 'b) t end
其中 (let+) 繫結到 map 操作,而 (and+) 繫結到單群 (monoidal) 乘積操作。
單子應提供一個實現以下介面的模組
module type Monad_syntax = sig include Applicative_syntax val ( let * ) : 'a t -> ('a -> 'b t) -> 'b t val ( and * ): 'a t -> 'b t -> ('a * 'b) t end
其中 (let*) 繫結到 bind 操作,而 (and*) 也繫結到單群乘積操作。
23.3 一般解糖規則
形式
let<op0>
x1 = e1
and<op1>
x2 = e2
and<op2>
x3 = e3
in e
會解糖為
( let<op0> )
(( and<op2> )
(( and<op1> )
e1
e2)
e3)
(fun ((x1, x2), x3) -> e)
當然,這適用於任何數量的巢狀 and 運算子。可以透過重複以下簡化步驟來表達一般規則
在
let<op0> x1 = e1 and<op1> x2 = e2 and... in e
中的第一個 and 運算子可以被解糖為一個函式應用
let<op0> (x1, x2) = ( and<op1> ) e1 e2 and... in e .
一旦所有 and 運算子都被簡化,則
let<op> x1 = e1 in e
中的 let 運算子可以被解糖為一個應用
( let<op> ) e1 (fun x1 -> e) .
請注意,文法允許在同一個綁定中混合不同的運算子符號(<op0> 、<op1> 、<op2> 可能不同),但我們強烈建議讓一起工作的 let 運算子和 and 運算子使用相同的符號。
23.4 變數綁定的簡短表示法 (let-punning)
(於 4.13.0 版本引入)
當要綁定的運算式是一個變數時,可以使用簡寫表示法 let+ x in ... ,它會展開為 let+ x = x in ... 。這種表示法,也稱為 let-punning,允許將上面的 sum3 函式更簡潔地寫成
open ZipSeq let sum3 z1 z2 z3 = let + z1 and + z2 and + z3 in z1 + z2 + z3
val sum3 : int Seq.t -> int Seq.t -> int Seq.t -> int Seq.t = <fun >
這種表示法也支援擴展節點,將 let%foo x in ... 展開為 let%foo x = x in ... 。然而,為了避免混淆,此表示法不支援一般的 let 綁定。
Copyright © 2024 Institut National de Recherche en Informatique et en Automatique