Spec-Zone.ru › OCaml
☰Язык OCaml
  • Язык OCaml
  • Расширения языка

Глава 12 Расширения языка

23 Операторы связывания

  • 23.1 Примеры
  • 23.2 Соглашения
  • 23.3 Общие правила десахаризации
  • 23.4 Краткая запись связывания переменных (let-punning)

(Добавлено в версии 4.08.0)

let-operator ::=
∣ let (core-operator-char ∣ <) { dot-operator-char }
and-operator ::=
∣ and (core-operator-char ∣ <) { dot-operator-char }
operator-name ::= ...
∣ let-operator
∣ and-operator
letop-binding ::= pattern = expr
∣ value-name
expr ::= ...
∣ let-operator letop-binding { and-operator letop-binding } in expr

Операторы связывания предоставляют синтаксический сахар, позволяющий использовать библиотечные функции с помощью варианта знакомого синтаксиса стандартных ключевых слов. В настоящее время поддерживаются «операторы связывания» 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.

« Обобщённые операторы openОбработчики эффектов »
Авторское право © 2026 Institut National de Recherche en Informatique et en Automatique

© 1995-2026 INRIA.
https://ocaml.org/manual/5.5/bindingops.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API