[ocaml] reference semantics

Landstreicher

Раньше думал, что в ML используется value semantics, а не reference semantics. Оказалось не так:

[lorien ~]$ ocaml
Objective Caml version 3.09.1
# let a = Array.make 3 "a";;
val a : string array = [|"a"; "a"; "a"|]
# let b = a;;
val b : string array = [|"a"; "a"; "a"|]
# Array.set a 1 "z";;
- : unit =
# b;;
- : string array = [|"a"; "z"; "a"|]
#

Почему так сделано? В C++ работает по-другому (и на мой взгляд, более логично).
Мне казалось, в функциональных языках значения обычно определяются один раз и потоэму любые изменения создают копию.

rosali

Изначально в ФЯ нет изменяемых данных, поэтому то, что '=' копирует именно ссылку - логично. Ссылку скопировать дешевле, а если нет разницы... Ну а когда потом в язык добавляются всякие эти Array и пр. получается вот так вот.

Landstreicher

У меня тогда такой вопрос возникает (на мой взгляд, естественный):
Допустим у меня есть некоторый сложный тип данных, внутри которого есть массив. И у меня создается большое количество объектов такого типа. Я хочу чтобы у каждого объекта этот массив был свой. В C++ для этого есть конструкторы копирования. А как быть в ML? Вдруг например, где-нибудь, массивы неявно скопируются по ссылке -- тогда операция с одним объектом испортит случайно данные другого объекта и отладить такую ошибку будет очень сложно.

Realist

Вероятно, в питоне происходит то же самое. Поэтому в Scons у класса Environment есть метод Copy.
В С++ большие объекты тоже обычно по ссылкам передаются. Поэтому более-менее с тем же успехом там тоже можно нечаяно попортить значение. Можешь привести пример неочевидной ошибки?

Ivan8209

Деструктивные действия --- занятия не для слабонервных.
Более естественное представление --- это списки.
Если ты захотел массивы, значит, ты хочешь побыстрее
и должен знать, что делаешь.
Решение: не использовать Array.set или перед этим делать копирование.
Тем более, что даётся подсказка:
- : unit =
значит, мимо не пройдёшь.
---
...Я работаю антинаучным аферистом...

pilot

Вероятно, в питоне происходит то же самое.

copy.deepcopy

Marinavo_0507

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

Landstreicher

На самом деле нужен был не массив, массив я привел как более простой пример, иллюстрирующий ту же идею. В действительности было нужно отображение (конечное) из одного множества в другое, что-то типа std::map. Я поискал и нашел Hashtbl. В нем обнаружилась описанная особенность.
То есть желание использовать такие структуры возникло не из желания сделать побыстрее, а потому что такой тип больше соотвествует смыслу задачи.
В результате пока заменил на список пар. Все вроде работает, но imho идеологически не очень правильно: список здесь лишний, в исходной задаче нет никакого списка. Есть конечное отображение из A в B. Разница примерно такая же, как между std::set и std::list. Возникают вопросы типа: если есть пары (a,b1) и (a,b2 то которое значение использовать? Хотелось чтобы сам тип данных не допускал появления подобных неоднозначностей (как например std::map). Непонятно, почему столь естественный и интуитивно понятный тип данных отсутствует в языке как встроенный, и приходится эмулировать его с помощью списка пар.

Marinavo_0507

> В нем обнаружилась описанная особенность.
Ну и нормально. Hashtbl - это императивная библиотека (в ocaml вообще в стандартной библиотеке чисто функциональных модулей мало). Поэтому всё просто: хочешь копировать - используй функцию copy, а оператор let подразумевает создание псевдонима, а не копирование.

rosali

Ну, шо ж ты творишь... Если по смыслу задачи нужно отображение, почему бы не воспользоваться отображением Благо ML язык высшего порядка.

vook

Я думаю хэш таблицы в этом языке тоже есть

rosali

Кста, не знаю как в caml-е но в SML есть стандартная библиотека отображений на бинарных деревьях, так она вся из себя аппликативная, то есть в ней нет понятия с-update-ить отображение, только создать новое.

Chupa

в ocaml есть map с чисто функциональной сигнатурой

rosali

Ну вот и надо пользовать. Хеш-таблицы это вообще зло

Landstreicher

Действительно, в OCaml тоже есть. Затупил, не сразу заметил. Читал какие-то факи и туториалы, там были List и Hashtbl, а Map не было.
Вопрос решен. Всем ответившим - большое спасибо!
PS. Работа с массивами и хэш-таблицами все равно не до конца понятна. Правильно ли я понимаю, что если я пишу программу в функциональном стиле, то все структуры данных тоже должны быть чисто функциональными? И если я пытаюсь использовать какой-нибудь Array или Hashtbl, то получаю мощный геморрой с тем, что нужно постоянно следить, кто где что поменял, кто где делает копии, а кто не делает итп?

Landstreicher

> Если по смыслу задачи нужно отображение, почему бы не воспользоваться отображением
> Благо ML язык высшего порядка.
Если ты предлагаешь использовать функции типа A -> B, то здесь это не подходит, так как с ними нужно делать действия типа:
1) сравнивать
2) находить область определения
3) сохранять в файл, читать из файла
Для Array, Hashtbl это просто делается. Как сравнивать абстрактные функции или записывать их в файл --- непонятно.

Ivan8209

> Правильно ли я понимаю, что если я пишу программу в функциональном стиле,
> то все структуры данных тоже должны быть чисто функциональными?
Я бы сказал, что желательно, но не обязательно.
> И если я пытаюсь использовать какой-нибудь Array или Hashtbl,
> то получаю мощный геморрой с тем, что нужно постоянно следить,
> кто где что поменял, кто где делает копии, а кто не делает итп?
Негде сейчас проверить, но разве настолько сложно отследить использование выражений с unit?
По-моему, должно быть достаточно усвоить, что let --- это bind или alias, а не copy.
То есть, если ты хочешь создать именно новый объект, то ты используешь copy, в остальных случаях --- let
(раз программа в функциональном стиле, то достаточно нового имени).
---
...Я работаю...

Landstreicher

> По-моему, должно быть достаточно усвоить, что let --- это bind или alias, а не copy.
> То есть, если ты хочешь создать именно новый объект, то ты используешь copy, в остальных
> случаях --- let
В общих чертах понял. Спасибо!
Оставить комментарий
Имя или ник:
Комментарий: