Re[15]: понимание ООП Алана Кея
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.03.23 05:48
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, Sinclair, Вы писали:

S>> ImmutableValue(T && value) : value_(value) {}
S>>Хм. А там точно будет ссылка на оригинал, а не дубликат?

V>Там будет и не ссылка, и не дубликат.


V>Тогда мы храним ссылку, а не значение, т.е. теряем контроль над другими потенциальными ссылками на это значение.

V>Я ж не просто так храню именно значение. ))

S>>Покажите мне пример кода, в котором вы изготавливаете из мутабельного объекта иммутабельный, и продемонстрируйте, что вы после этого не можете этот иммутабельный изменить.


V>
V>template<typename T>
V>class ImmutableValue {
V>    const T value_;
V>public:
V>    ImmutableValue(T && value) : value_(move(value)) {}
V>    const T & value() const { return value_; }
V>};

V>int main()
V>{
V>    using namespace std;
V>    typedef ImmutableValue<string> istring;

V>    string s1 = "it is a long enough string";
V>    istring s2 = move(s1);
V>    assert(s2.value() == "it is a long enough string");
V>    assert(s1 == "");

V>    s1 = "42";
V>    assert(s2.value() == "it is a long enough string");
V>    assert(s1 == "42");

V>    return 0;
V>}
V>

Понятно. Вижу, вы исправили ошибку — теперь у вас в коде 2 move. Исходный код, который вы привели, падал на assert(s1 == "").

V>Здесь "it is a long enough string/0" выделяется в динамической памяти, затем через std::move перемещается в иммутабельную строку s2, при этом строка s1 теряет своё содержимое.


V>Далее мы изменяем значение строки s1, в то время как зачение s2 не изменяется.

V>Изменить законными способами значение переменной s2 нельзя;
Осталось понять, что изменится, если мы поубираем ключевые слова const из вашего шаблонного кода. Перестанет ли isting быть иммутабельным?

V>
V>    istring s3 = s2; // создание копии строки
V>    assert(s3.value() == "it is a long enough string");
V>    assert(s2.value().data() != s3.value().data()); // именно копии, по разным адресам

V>    s2 = s3; // ошибка компиляции: operator=(const ImmutableValue<std::string> &) удалён
V>

V>Последняя строка тоже важна для понимания — получив однажды ссылку на ImmutableValue<std::string> я могу быть уверен, что переменную не затрут.
Вот этот фрагмент непонятен. Зачем вы производите копии иммутабельного объекта? Он же гарантированно иммутабельный, это как раз и даёт ссылочную прозрачность.

V>>>Далее в своём коде используешь шаблонный ImmutableMap.

S>>Вот прямо так сразу использовать шаблонный ImmutableMap не получится. Вы, как водится, привели тот самый фрагмент кода, который был очевиден и без обсуждения.
S>>Как вы реализуете метод add(const TKey & key, const TValue & value)?
V>Никак.
V>Для иммутабельных типов это будут внешние ф-ии и операторы, разумеется.
V>Просто в дотнете сплошные методы. ))
Методы гораздо удобнее выстраивать в цепочку. Запись вида list.add(1).add(2).add(42) читать гораздо комфортнее, чем add(add(add(list, 1), 2), 42))).
Но это дело вкуса. Покажите, как вы будете реализовывать внешний метод add.

V>Можно.



V>Зря сомневаешься.

Ну, не с первого раза, но, вроде бы, удалось. Правда, ваш тип зачем-то создаёт избыточные копии на ровном месте, ну это ладно. Наверное, можно научить людей избегать передачи по значению, хоть это и неудобно.

V>Это смотря, какова природа/структура и объемы данных.


V>copy-on-write для небольших объемов линейно-расположенных в памяти данных работает лучше иммутабельного графа.
Началось виляние. Узнаю коллегу вдимаса.
V>Но это мы уже ответвились в другую область — в область алгоритмов и структур данных, а они зависят от конкретных задач (или классов задач, бо практически все задачи уже известны, бгг).
Конкретная задача хорошо известна — реализовать иммутабельные структуры так, чтобы они были а) надёжными, б) эффективными.

V>В моём случае не придётся для сценария, скажем, для которого разработали FrozenDictionary в 8-м дотнете.

Да, для этого экзотического сценария можно сделать реализацию эффективнее, чем ImmutableDictionary. Обратите внимание на две вещи:
1. Ваша реализация оказалась уместна только для экзотического частного случая; для более общего частного случая она непригодна.
2. В дотнете как-то обошлись без магии ключевого слова const. Выходит, любые виды неизменности реализуемы и без него. Сюрприз-сюрприз!

V>И странно, что оно не было сделано еще лет 20 назад, когда тема иммутабельных деревьев была модной на слуху.

Сначала удовлетворяются потребности 90% аудитории. Потом — 9%. Потом — 0.9%. Вот теперь дошла очередь и до 0.1%.

V>В C# ключевое слово const у мемберов объявляет аналог статического readonly поля объекта.




V>Тебе нужны были железобетонные гарантии — ну и вот.

Так гарантии по-прежнему обеспечены дизайном класса, а не ключевым словом const.

V>Если же речь об алгоритмах и структурах данных — ну так пиши эти структуры и алгоритмы над ними.

V>Весь механизм для этого есть.


V>Это именно реализация некоторых иммутабельных алгоритмов над некоторыми иммутабельными структурами данных.


V>Collections.Immutable не приносит в язык иммутабельность как таковую, эта либа лишь реализует некоторые структуры и алгоритмы, доказавшие свою полезность в ФП.
По-видимому, вы так и не понимаете, что такое иммутабельность как таковая.


V>Я могу нарушать гарантии интерфейсов, например, реализовывать тип как мутабельный и возвращать this из методов.

Интерфейсов — да. Классов — нет.

S>>Вот мой метод:

S>>
S>>public void RegisterState(ImmutableDictionary<string, int> state)
S>>{
S>>  _visited[state] = true;
S>>}
S>>

S>>Попробуйте "сломать" его, передав в него мутабельный state.

V>У тебя ошибка, должно быть так:

Нет никакой ошибки. state — immutable, _visited — mutable.

V>И это у тебя оно гарантируется из-за использования конкретного типа ImmutableDictionary<,>, а если сделать так:

V>
V>public void RegisterState<TDict, TKey, TValue>(TDict state)
V>    where TDict : IImmutableDictionary<TKey, TValue> {...}
V>

V>то прощай гарантии, можно начинать хулиганить. ))
Ну, так поэтому я и не стал писать так, чтобы вы начинали хулиганить.

V>В плюсах, напротив, можно определить некий концепт ImmutableMap и юзать так:

V>
V>template<ImmutableMap TDict>
V>void RegisterState(TDict state) {
V>   visited_ = merge(visited_, state, true);
V>}
V>

V>Где концепт ImmutableMap требует, например, быть инстансом шаблонного типа ImmutableMapWrapper, которому в кач-ве аргумента подали тип, удовлетворяющий публичному интерфейсу std::map.
И чем это будет отличаться от конкретного ImmutableMapWrapper<std::map>?

V>Что касается ключевой фишки иммутабельных списков/графов, т.е. что касается шаренья узлов между инстансами, этого можно достичь или через счётчики ссылок, или через техники навроде региональной памяти, чтобы не возиться со счётчиками.

Эффективность упадёт на дно.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.