☰ OCaml 语言
第 12 章 语言扩展
23 绑定运算符
(在 4.08.0 中引入)
绑定运算符提供语法糖,以便在(标准关键字的变体)熟悉的语法下公开库函数。目前支持的“绑定运算符”是 let<op> 和 and<op> ,其中 <op> 是一个运算符符号,例如 and+$ 。
引入绑定运算符是为了为使用 monad 和 applicative functor 提供方便的语法;对于那些,我们建议使用运算符 * 和 + 分别进行约定。它们可以用于其他目的,但应该记住,每个引入的新不熟悉的符号都会使程序对于非专家来说更难理解。我们预计随着时间的推移,将开发出关于其他运算符系列的新约定。
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 约定
一个 applicative functor 应该提供一个实现以下接口的模块
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 乘积操作。
一个 monad 应该提供一个实现以下接口的模块
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*) 也绑定到 monoidal 乘积操作。
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 运算符。可以通过重复以下简化步骤来表达一般规则
第一个 and 运算符在
let<op0> x1 = e1 and<op1> x2 = e2 and... in e
可以反糖化为函数应用
let<op0> (x1, x2) = ( and<op1> ) e1 e2 and... in e .
一旦所有 and 运算符都简化掉了,let 运算符在
let<op> x1 = e1 in e
可以反糖化为应用
( let<op> ) e1 (fun x1 -> e) .
请注意,语法允许在同一绑定中混合不同的运算符符号 (<op0> ,<op1> ,<op2> 可能不同),但我们强烈建议 API 在一起工作的 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 绑定。
版权所有 © 2024 法国国家信息与自动化研究所