Глава 12 Расширения языка
23 Операторы связывания
- 23.1 Примеры
- 23.2 Конвенции
- 23.3 Общие правила десугаринга
- 23.4 Короткая запись для связывания переменных (let-punning)
(Введено в версии 4.08.0)
|
Операторы связывания предлагают синтаксический сахар для экспонирования функций библиотеки в (варианте) знакомой синтаксической конструкции стандартных ключевых слов. В настоящее время поддерживаются «операторы связывания» let<op> и and<op>, где <op> — символ оператора, например and+$.
Операторы связывания были введены для удобного синтаксиса работы с монадами и аппликативными функторами; для них мы предлагаем конвенции, использующие операторы * и + соответственно. Они могут использоваться и для других целей, но следует помнить, что каждое новое незнакомое обозначение усложняет понимание программ для неспециалистов. Мы ожидаем, что новые конвенции будут разрабатываться со временем и для других семейств операторов.
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 =
val return : 'a -> 'a option = а затем применять их, используя удобный синтаксис:
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 = что эквивалентно следующей развернутой форме:
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 = Пользователи также могут определять операторы 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 = что эквивалентно следующей развернутой форме:
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 = 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+) — к операции монадного произведения.
Моната должен предоставить модуль, реализующий следующий интерфейс:
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. Можно выразить общее правило, повторяя следующие шаги упрощения:
- Первый оператор 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 =
Это обозначение также поддерживается для расширений узлов, преобразуя let%foo x in ... в let%foo x = x in .... Однако, чтобы избежать путаницы, это обозначение не поддерживается для простых связываний let.
© 1995-2024 INRIA.
https://ocaml.org/manual/5.2/bindingops.html