?

Log in

No account? Create an account

Previous Entry | Next Entry

Не только лишь все знают, что в 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!

Comments

( 3 comments — Leave a comment )
iamjaph
Jan. 16th, 2019 08:16 am (UTC)
А когда вы, обычно, используете f (x, y), а когда f x y?
Сам вижу ясно только случаи:
* первый - когда (x, y) - это тип
* второй - когда реально нужно каррирование для передачи f x куда-то.
А вот в других вариантах трудно определиться.
rdia
Jan. 16th, 2019 11:28 am (UTC)
Сейчас традиционно используется второй вариант f x y. Это практичнее, поскольку язык позволяет делать частичное применение. То есть, вместо


let result = List.map (fun y -> f(1,y)) []


писать

let result = List.map (f 1) []


Если частичным применением ее злоупотреблять, то получается проще и приятнее для чтения.

Поэтому сейчас в кортеж оборачивается строго по нужде - когда надо вернуть из функции несколько значений. Или, как вы говорите - тип такой.

Из типовых ситуаций, на ум приходит лишь fold_left, с хитрым аккумулятором.

Edited at 2019-01-16 11:33 am (UTC)
iamjaph
Jan. 16th, 2019 12:34 pm (UTC)
Спасибо.
( 3 comments — Leave a comment )

Latest Month

February 2019
S M T W T F S
     12
3456789
10111213141516
17181920212223
2425262728  

Page Summary

Powered by LiveJournal.com
Designed by Tiffany Chow