Поддерживает ли OCaml передачу параметров вызова по имени?

Scala позволяет параметрам функции быть вызов по имени с использованием синтаксиса «=>». Например, логическая функция and может быть определена как:

def and(x: Boolean, y: => Boolean): Boolean =
  if (x) y else false

Второй параметр — вызов по имени. Это гарантирует ожидаемое поведение «короткого замыкания» — если x равно false, y вообще не будет оцениваться.

В общем, это полезно для функций, в которых параметр может вообще не использоваться.

Как это можно сделать в OCaml?

Я знаю, что в OCaml есть конструкции для ленивых вычислений, но мне не ясно, можно ли их использовать для передачи параметров вызова по имени и если да, то как.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
68
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Во-первых, по своей конструкции булевы операторы ленивы:

# false || (print_endline "second"; true);;
second
- : bool = true
# true || (print_endline "second"; true);;
- : bool = true
# true && (print_endline "second"; false);;
second
- : bool = false
# false && (print_endline "second"; false);;
- : bool = false

Затем, для большего количества пользовательских функций, вы действительно должны использовать lazy (с огромным преимуществом, что ленивые значения запоминаются после оценки):

let fib n =
  let rec aux n b a =
    Printf.printf "%d %d %d\n" n b a;
    if n <= 0 then a else aux (n - 1) (a + b) b
  in
  aux n 1 0

let f a b = if b > 0 then Lazy.force a + 1 else b
(* val f : int lazy_t -> int -> int = <fun> *)

let () =
  let r = lazy (fib 10) in
  let r2 = lazy (fib 1000000) in
  Printf.printf "%d\n" (f r 1 + f r 2 + f r2 0)

Результат будет:

10 1 0
9 1 1
8 2 1
7 3 2
6 5 3
5 8 5
4 13 8
3 21 13
2 34 21
1 55 34
0 89 55
112

r оценивался только один раз, а r2 никогда не оценивался.

Я понял из вашего ответа, что если мы попытаемся определить оператор and таким образом, он будет иметь тип bool -> bool lazy_t -> bool, что потребует вызовов для переноса второго аргумента в lazy. Если это так, то как на самом деле определяются логические операторы в OCaml? Являются ли они просто внутренними и не могут быть определены в самом языке?

Nicholas Weston 27.03.2022 12:17

Логические операторы определены как примитивы компилятора (см. github.com/ocaml/ocaml/blob/trunk/lambda/translprim.ml#L139), но я никогда не копался так глубоко в компиляторе, чтобы узнать, как они написаны и что они делают. Если кто-то вроде @gasche хотел бы кратко объяснить это здесь, это было бы неплохо

Lhooq 27.03.2022 13:17

Я не уверен, что тут объяснять? И ||, и && определены как примитивы компилятора в стандартной библиотеке с external (||): bool -> bool -> bool = "%seqor" и external (&&): bool -> bool -> bool = "%seqand" (имена примитивов компилятора начинаются с %). И компилятор реализует обычную семантику короткого замыкания для %seqor и %seqand.

octachron 27.03.2022 14:10

Вы на самом деле указали на то, что нужно объяснить, And the compiler is implementing the usual short-circuiting semantics for %seqor and %seqand. Поскольку оп спросил Are they just intrinsic, and unable to be defined in the language itself?, я ответил, что они являются внутренними, но я не думаю, что их можно определить в самом языке, но я не смог бы это объяснить. Вот и все, «можете ли вы определить такой оператор, как &&, не используя lazy?». Я чувствую, что вы не можете, но я не могу объяснить, что для них делает компилятор.

Lhooq 27.03.2022 19:36

Я действительно не понимаю вашего комментария @octachron. Я написал, что логические операторы определены как примитивы компилятора, но я не знаю, как они компилируются, и, следовательно, не могу объяснить, что за этим стоит, а вы прокомментировали: «логические операторы являются примитивами компилятора, и компилятор их компилирует, я не знаю». не знаю, что тут объяснять». Мой вопрос был конкретно о том, «как они компилируются и выполнимо ли это в программе OCaml?»

Lhooq 27.03.2022 19:40

Возможно, лучше было бы сформулировать, что никакой OCaml не всегда вызывается по значению, и || не может быть реализован как функция больше, чем if ... then ... else... Другими словами, x || y и x && y являются сокращением семантики для if x then true else y и if x then y else false с обычной семантикой для if ... then ... else. В частности, ((||) true) (Printf.printf "not short-circuiting\n"; true) печатает на стандартный вывод, потому что (||) true покупает замыкание, которое больше не является примитивом компилятора.

octachron 27.03.2022 20:03

Но sequand и sequor определены в файлах OCaml, поэтому я не совсем понимаю, почему вы не могли их переписать самостоятельно. Если бы они были примитивами в файлах C, я бы это понял. Но это не так.

Lhooq 27.03.2022 20:26

Семантика этих примитивов определяется компилятором. Чтобы переписать || в OCaml, вам нужно будет закодировать примитив %seqand в другой примитив, например if ... then ... else .... Обратите внимание, что компилятор OCaml полностью написан на OCaml, только среда выполнения написана на C. Тем не менее, семантика языка реализации компилятора не имеет отношения к семантике скомпилированных программ. Например, вы можете полностью написать компилятор Haskell на OCaml.

octachron 27.03.2022 21:01

Я полагаю, что более простой способ, которым я мог бы задать свой дополнительный вопрос, заключается в том, можно ли определить функцию с параметром вызова по имени таким образом, чтобы обеспечить тот же интерфейс для вызывающей стороны, что и функция вызова по значению . Так обстоит дело в Scala и в логических операторах OCaml. Но для пользовательских функций в OCaml, как я понял из этого разговора, ответ отрицательный.

Nicholas Weston 28.03.2022 09:29

Другие вопросы по теме