Не только лишь все знают, что в Ocaml'е параметры функций
вычисляются немножко по-арабски - справа налево. И это суровая
оптимизационная необходимость, описанная в книге Ксавье Леруа
"The ZINC experiment: an economical implementation of the ML language",
которую можно скачать у него на сайте
https://xavierleroy.org/bibrefs/Leroy-ZINC.html,
(страница 14).
То есть, если мы загрузим REPL и напишем
(fun _ _ -> ()) (print_string "Left") (print_string "Right")
мы получим "RightLeft". Ничего вроде бы интересного - что заказывали,
то и получили.
Теперь вспомним, что в ML в 80-х происходил переход от кортежей
к каррированным функциям. То есть, вместо f (x, y) люди начали писать
f x y (в библиотеке SML Basis ещё остались следы древних времён).
То есть, немного предсказуемо, из
(fun _ -> ()) (print_string "Left", print_string "Right")
мы получаем опять "RightLeft". Вроде бы так и должно быть - каррированная
функция f x y могла быть записана программистом как f(x,y), а значит оба
варианта должны иметь одинаковые побочные эффекты, чтобы сюрпризов было
по-меньше.
Тем не менее, (x,y) - это кортеж. Соответственно, порядок инициализации полей
кортежа (a, b, c, d) - справа налево. Для проверки
(print_string "Left", print_string "Middle", print_string "Right")
в REPL действительно даёт "RightMiddleLeft".
Уже занятно, но мы же последовательные люди, и у нас в наличии масса разных скобок.
Проверив
[ print_string "Left"; print_string "Middle"; print_string "Right"]
и
[| print_string "Left"; print_string "Middle"; print_string "Right"|]
убеждаемся, что и списки, и массивы инициализируются по-арабски, справа налево.
Аналогично работает инициализация полей структур:
type r = { a : unit; b : unit; c : unit }
let _ = { a = print_string "Left"; b = print_string "Middle"; c = print_string "Right" }
И, что самое поразительное, точно также работают и функторы - программа
module type S = sig val x : unit end
module F : functor (X : S) (Y : S) -> struct end
module C = F (struct let x = print_string "Left" end)
(struct let x = print_string "Right" end)
выводит "RightLeft". С объектами дела обстоят точно также - new obj a b вычислит сперва b, затем a.
И вроде бы всё безумно одинаково, последовательно, скучно и совершенно нехарактерно
для эклектичного Ocaml, но идиллию портят
(print_string "Left"; print_string "Middle"; print_string "Right")
выводящее "LeftMiddleRight", как и положено блоку последовательных команд, да
{|print_string "Left"; print_string "Middle"; print_string "Right"|}
которое вообще строка. И, всё-таки, это Ocaml!