Глава 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-2026 INRIA.
https://ocaml.org/manual/5.5/bindingops.html