Извините за waffly title - если бы я мог придумать краткое название, мне не пришлось бы задавать вопрос.
Предположим, что у меня есть неизменный тип списка. Он имеет операцию Foo(x)
, которая возвращает новый неизменяемый список с указанным аргументом в качестве дополнительного элемента в конце. Поэтому для создания списка строк со значениями "Hello", "immutable", "world" вы можете написать:
var empty = new ImmutableList<string>();
var list1 = empty.Foo("Hello");
var list2 = list1.Foo("immutable");
var list3 = list2.Foo("word");
(Это код С#, и меня больше всего интересует предложение С#, если вы считаете, что язык важен. Это не фундаментальный вопрос языка, но идиомы языка могут быть важными.)
Важно то, что существующие списки не изменяются Foo
- поэтому empty.Count
все равно вернет 0.
Другим (более идиоматическим) способом достижения конечного результата будет:
var list = new ImmutableList<string>().Foo("Hello")
.Foo("immutable")
.Foo("word");
Мой вопрос: какое лучшее имя для Foo?
РЕДАКТИРОВАТЬ 3. Как я узнал позже, имя типа не может быть на самом деле ImmutableList<T>
, что делает положение четким. Представьте себе, что он TestSuite
и что он неизменен, потому что вся инфраструктура, в которой она входит, является неизменной...
(Конец редактирования 3)
Параметры, которые я придумал до сих пор:
-
Add
: общий в .NET, но подразумевает мутацию исходного списка -
Cons
: Я считаю, что это нормальное имя в функциональных языках, но бессмысленное для тех, кто не имеет опыта работы на таких языках. -
Plus
: мой любимый до сих пор, это не означает мутации для меня. По-видимому, это также используемое в Haskell, но с немного отличающимися ожиданиями (программист Haskell может ожидать, что он добавит два списка вместе, а не добавит одно значение к другой список). -
With
: согласуется с некоторыми другими неизменяемыми соглашениями, но не имеет совершенно такой же "дополнительности" для него IMO. -
And
: не очень описательный. - Перегрузка оператора для +: мне это очень не нравится; Обычно я считаю, что операторы должны применяться только к более низким уровням. Я готов быть убежденным, хотя!
Критерии, которые я использую для выбора:
- Дает правильное представление о результате вызова метода (т.е. его исходный список с дополнительным элементом)
- Делает это настолько ясным, насколько возможно, что он не мутирует существующий список
- Звучит разумно, когда соединяется вместе, как во втором примере выше
Пожалуйста, задайте более подробную информацию, если я недостаточно четко проясню...
РЕДАКТИРОВАТЬ 1: Здесь мои аргументы в пользу предпочтения Plus
- Add
. Рассмотрим эти две строки кода:
list.Add(foo);
list.Plus(foo);
По моему мнению (и это личное дело), последний явно ошибочен - ему нравится писать "x + 5"; как выражение само по себе. Первая строка выглядит как хорошо, пока вы не вспомните, что она неизменна. Фактически, то, что оператор плюс сам по себе не мутирует его операнды, является еще одной причиной, по которой Plus
является моим фаворитом. Без малейшей небрежности перегрузки оператора он все же дает те же коннотации, которые включают (для меня) не мутирование операндов (или целевого метода в этом случае).
РЕДАКТИРОВАТЬ 2: Причины нежелания добавлять.
Эффектны разные ответы: "Go with Add. Что делает DateTime
, а String
имеет методы Replace
и т.д., которые не делают неизменность очевидной". Я согласен - здесь есть приоритет. Тем не менее, я видел, что многие люди называют DateTime.Add
или String.Replace
и ожидают мутации. Есть множество вопросов о новостях (и, возможно, SO, если я копаю), на которые отвечает "Вы игнорируете возвращаемое значение String.Replace
, строки неизменяемы, новая строка возвращается".
Теперь я должен раскрыть тонкость вопроса - этот тип не может быть действительно неизменным, но другим неизменным типом. В частности, я работаю над базой тестов, где вы добавляете тесты в набор, и это создает новый пакет. Очевидно, что:
var list = new ImmutableList<string>();
list.Add("foo");
не собирается ничего делать, но при изменении его становится намного мрачнее:
var suite = new TestSuite<string, int>();
suite.Add(x => x.Length);
Похоже, все должно быть хорошо. Если это, для меня, делает ошибку более ясной:
var suite = new TestSuite<string, int>();
suite.Plus(x => x.Length);
Это просто попрошайничество:
var suite = new TestSuite<string, int>().Plus(x => x.Length);
В идеале я бы хотел, чтобы мои пользователи не нуждались в том, чтобы сказать, что набор тестов неизменен. Я хочу, чтобы они попали в пропасть успеха. Это может быть невозможно, но я бы хотел попробовать.
Извиняюсь за чрезмерное упрощение исходного вопроса, говоря только о неизменяемом типе списка. Не все коллекции такие же самоописательные, как ImmutableList<T>
:)