Какое лучшее имя для не-мутирующего метода "добавить" к неизменяемой коллекции?

Извините за 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>:)

Ответ 1

В подобных ситуациях я обычно иду с Concat. Обычно это означает, что создается новый объект.

var p = listA.Concat(listB);
var k = listA.Concat(item);

Ответ 2

Я бы пошел с Cons, по одной простой причине: это означает именно то, что вы хотите.

  • Я большой поклонник сказать, что я имею в виду, особенно в исходном коде. Новичкам придется искать определение Cons только один раз, но затем читать и использовать это тысячу раз. Я считаю, что в долгосрочной перспективе лучше работать с системами, упрощающими общий случай, даже если начальная стоимость немного выше.

  • Тот факт, что это было бы "бессмысленно" для людей без опыта работы с FP, на самом деле является большим преимуществом. Как вы указали, все другие слова, которые вы нашли, уже имеют определенный смысл, и этот смысл либо немного отличается, либо неоднозначен. Новая концепция должна иметь новое слово (или, в данном случае, старое). Я бы предпочел, чтобы кто-то искал определение "Против", чем предположить, что он знает, что делает "Добавить".

  • Другие операции, заимствованные из функциональных языков, часто содержат свои оригинальные имена без каких-либо явных катастроф. Я не видел никакого толчка, чтобы придумать синонимы для "карты" и "уменьшить" звук, более знакомый не-FPers, и я не вижу никакой пользы от этого.

(Полное раскрытие: я программист Lisp, поэтому я уже знаю, что означает "Cons" ).

Ответ 3

На самом деле мне нравится And, особенно по идиоматическому пути. Мне особенно понравилось бы, если бы у вас было статическое свойство readonly для пустого списка и, возможно, сделало бы его конструктором частным, поэтому вам всегда нужно строить из пустого списка.

var list = ImmutableList<string>.Empty.And("Hello")
                                      .And("Immutable")
                                      .And("Word");

Ответ 4

Всякий раз, когда я нахожусь в пробке с номенклатурой, я попадаю в interwebs.

thesaurus.com возвращает это для "add":

Определение: примыкает, увеличивается; делать дальнейший комментарий

Синонимы: аффикс, приложение, анте, добавление, увеличение, говядина повышать, наращивать, накапливать, продолжайте, подскажите, рисуйте, плоть выходить, нагреваться, подниматься, подниматься, замириться, подключить, подключить, включить, разъем вверх, джаз вверх, объединить, проложить, Parlay, контрейлерные, вставить, вылить ответить, подбежать, сказать дальше, пощечину на, снежный ком, суп вверх, ускорение, шип, наращивать, добавлять, подсластить, tack on, tag

Мне нравится звук Adjoin или проще Join. Это то, что вы делаете, не так ли? Этот метод может также применяться для объединения других ImmutableList<>.

Ответ 5

Лично мне нравится .With(). Если бы я использовал объект, прочитав документацию или комментарии к коду, было бы ясно, что он делает, и он читает ok в исходном коде.

object.With("My new item as well");

Или вы добавляете "Вдоль" с ним..:)

object.AlongWith("this new item");

Ответ 6

Я закончил с добавлением для всех моих неизменных коллекций в BclExtras. Причина в том, что это легко предсказуемое имя. Я не очень беспокоюсь о том, что люди путают Add с мутирующим добавлением, так как имя типа имеет префикс Immutable.

На какое-то время я считал Cons и другие имена функциональных стилей. В конце концов я обесценил их, потому что они не так хорошо известны. Конечно, функциональные программисты поймут, но они не большинство пользователей.

Другие имена: вы упомянули:

  • Плюс: я желаю/стираю это. Для меня это не отличает его как не мутирующую операцию, чем Add does
  • С: вызовет проблемы с VB (каламбур)
  • Перегрузка оператора: открытость будет проблемой

Варианты, которые я рассматривал:

  • Concat: String являются неизменяемыми и используют это. К сожалению, это действительно очень полезно для добавления в конец
  • CopyAdd: скопировать что? Источник, список?
  • AddToNewList: Может быть, хороший для List. Но как насчет коллекции, стека, очереди и т.д.?

К сожалению, на самом деле не похоже слово

  • Определенно неизменяемая операция
  • Понятно большинству пользователей
  • Представляем менее чем за 4 слова

Это становится еще более странным, если вы рассматриваете коллекции, отличные от List. Возьмем, например, Stack. Даже программисты первого года могут сказать вам, что у стеков есть метод Push/Pop. Если вы создадите ImmutableStack и дадите ему совершенно другое имя, позвоните ему Foo/Fop, вы просто добавили больше работы для их использования в своей коллекции.

Изменить: Ответ на Plus Edit

Я вижу, куда вы идете с Плюсом. Я думаю, что более сильным случаем будет Минус для удаления. Если бы я увидел следующее, я бы наверняка задался вопросом, что в мире думал программист.

list.Minus(obj);

Самая большая проблема, с которой я сталкиваюсь с Plus/Minus или новым спариванием, - это похоже на перебор. Сама коллекция уже имеет отличительное имя, неизменяемый префикс. Зачем идти дальше, добавляя словарный запас, целью которого является добавление того же различия, что и неизменяемый префикс.

Я могу увидеть аргумент сайта вызова. Это делает его более ясным с точки зрения одного выражения. Но в контексте всей функции это кажется ненужным.

Изменить 2

Согласитесь, что люди определенно были смущены String.Concat и DateTime.Add. Я видел, как несколько очень ярких программистов справились с этой проблемой.

Однако я думаю, что ImmutableList - это другой аргумент. Нет ничего о String или DateTime, который устанавливает его как неизменяемый для программиста. Вы должны просто знать, что он неизменен через какой-то другой источник. Поэтому беспорядок не является неожиданным.

У ImmutableList нет этой проблемы, потому что имя определяет ее поведение. Вы могли бы утверждать, что люди не знают, что такое Immutable, и я считаю, что это также справедливо. Я, конечно, не знал об этом до 2 года в колледже. Но у вас есть одна и та же проблема с любым именем, которое вы выбираете, вместо Add.

Изменить 3: Как насчет таких типов, как TestSuite, которые неизменяемы, но не содержат слова?

Я думаю, что это приводит домой идею о том, что вы не должны изобретать новые имена методов. А именно потому, что есть явно стремление сделать типы неизменяемыми, чтобы облегчить параллельные операции. Если вы сосредоточитесь на изменении имени методов для коллекций, следующим шагом будет имя метода мутирования для каждого используемого вами типа, который является неизменным.

Я думаю, что было бы более ценным усилием вместо этого сосредоточиться на том, чтобы типы, идентифицируемые как Неизменяемые. Таким образом, вы можете решить проблему, не переосмысливая каждый шаблон метода мутации.

Теперь, как вы можете определить TestSuite как неизменяемый? В сегодняшней среде я думаю, что есть несколько способов

  • Префикс с неизменяемым: ImmutableTestSuite
  • Добавить атрибут, который описывает уровень Immutablitiy. Это, конечно, менее доступно для обнаружения.
  • Не больше.

Моя догадка/надежда - это инструменты для разработки, которые помогут справиться с этой проблемой, упрощая идентификацию неизменяемых типов просто по виду (разный цвет, более сильный шрифт и т.д.). Но я думаю, что ответ, хотя и изменил все имена методов.

Ответ 7

Я думаю, что это может быть одна из тех редких ситуаций, когда допустимо перегружать оператор +. В математической терминологии мы знаем, что + не добавляет что-то к концу чего-то еще. Он всегда объединяет два значения вместе и возвращает новое результирующее значение.

Например, это интуитивно очевидно, что, когда вы говорите

x = 2 + 2;

результирующее значение x равно 4, а не 22.

Аналогично,

var empty = new ImmutableList<string>();
var list1 = empty + "Hello";
var list2 = list1 + "immutable";
var list3 = list2 + "word";

должен четко указывать, что каждая переменная будет удерживать. Должно быть ясно, что list2 не изменяется в последней строке, но вместо этого list3 присваивается результат добавления слова "<" к list2.

В противном случае я бы назвал функцию Plus().

Ответ 8

Чтобы быть как можно более ясным, вы можете пойти с wordier CopyAndAdd или что-то подобное.

Ответ 9

Я бы назвал его Extend() или, возможно, ExtendWith(), если вы чувствуете себя действительно многословным.

Расширения означает добавление чего-то к чему-то другому, не меняя его. Я думаю, что это очень актуальная терминология на С#, поскольку это похоже на концепцию методов расширения - они "добавляют" новый метод в класс без "касания" самого класса.

В противном случае, если вы действительно хотите подчеркнуть, что вы вообще не изменяете исходный объект, используйте некоторый префикс, подобный Get-, как неизбежный для меня.

Ответ 10

Мне нравится предложение mmyers CopyAndAdd. В соответствии с темой "мутации", возможно, вы можете пойти с Bud (бесполое воспроизведение), Grow, Репликация или Evolve? =)

EDIT: продолжение моей генетической темы, как насчет Procreate, подразумевая, что создается новый объект, основанный на предыдущем, но с чем-то новым.

Ответ 11

Добавлено(), добавлено()

Мне нравится использовать прошедшее время для операций над неизменяемыми объектами. Он передает идею о том, что вы не меняете оригинальный объект, и его легко распознать, когда вы его видите.

Кроме того, поскольку имена методов мутации часто представляют собой текущие глаголы, они применимы к большинству случаев, которые вам требуются в отношении неизменяемого метода. Например, неизменяемый стек имеет методы "push" и "popped".

Ответ 12

Это, вероятно, растяжка, но в Ruby есть общепринятое обозначение для различия: add не мутирует; add! мутирует. Если это широко распространенная проблема в вашем проекте, вы тоже можете это сделать (не обязательно с неалфавитными символами, но последовательно используя обозначение, чтобы указать мутирующие/не мутирующие методы).

Ответ 13

Join представляется подходящим.

Ответ 14

Возможно, путаница проистекает из того факта, что вы хотите две операции в одном. Почему бы не разделить их? Стиль DSL:

var list = new ImmutableList<string>("Hello");
var list2 = list.Copy().With("World!");

Copy будет возвращать промежуточный объект, который изменяет копию исходного списка. With вернет новый неизменный список.

Update:

Но наличие промежуточной, изменчивой коллекции вокруг не является хорошим подходом. Промежуточный объект должен содержаться в операции Copy:

var list1 = new ImmutableList<string>("Hello");
var list2 = list1.Copy(list => list.Add("World!"));

Теперь операция Copy принимает делегат, который получает измененный список, чтобы он мог контролировать результат копирования. Это может сделать гораздо больше, чем добавление элемента, например удаление элементов или сортировка списка. Его также можно использовать в конструкторе ImmutableList для сборки исходного списка без промежуточных неизменяемых списков.

public ImmutableList<T> Copy(Action<IList<T>> mutate) {
  if (mutate == null) return this;
  var list = new List<T>(this);
  mutate(list);
  return new ImmutableList<T>(list);
}

Теперь нет возможности ошибочного толкования пользователями, они будут естественно попадать в яму успеха.

Еще одно обновление:

Если вам все еще не нравится упоминание измененного списка, даже теперь, когда оно содержится, вы можете создать объект спецификации, который будет указывать, или script, как операция копирования преобразует его список. Использование будет таким же:

var list1 = new ImmutableList<string>("Hello");
// rules is a specification object, that takes commands to run in the copied collection
var list2 = list1.Copy(rules => rules.Append("World!"));

Теперь вы можете быть творческим с именами правил, и вы можете показывать только те функции, которые хотите Copy, а не все возможности IList.

Для использования цепочки вы можете создать разумный конструктор (который, конечно же, не будет использовать цепочку):

public ImmutableList(params T[] elements) ...

...

var list = new ImmutableList<string>("Hello", "immutable", "World");

Или использовать один и тот же делегат в другом конструкторе:

var list = new ImmutableList<string>(rules => 
  rules
    .Append("Hello")
    .Append("immutable")
    .Append("World")
);

Это предполагает, что метод rules.Append возвращает this.

Это будет выглядеть в вашем последнем примере:

var suite = new TestSuite<string, int>(x => x.Length);
var otherSuite = suite.Copy(rules => 
  rules
    .Append(x => Int32.Parse(x))
    .Append(x => x.GetHashCode())
);

Ответ 15

Несколько случайных мыслей:

  • ImmutableAdd()
  • Append()
  • ImmutableList <T> (ImmutableList <T> originalList, T newItem) Конструктор

Ответ 16

DateTime в С# использует Add. Так почему бы не использовать одно и то же имя? Пока пользователи вашего класса понимают, что класс неизменен.

Ответ 17

Я думаю, что ключевая вещь, которую вы пытаетесь понять, трудно выразить, это непермутация, поэтому, возможно, что-то с порождающим словом в ней, что-то вроде CopyWith() или InstancePlus().

Ответ 18

Я не думаю, что английский язык позволит вам подразумевать неизменность безошибочно, используя глагол, который означает то же самое, что и "Добавить". "Плюс" почти делает это, но люди все еще могут ошибаться.

Единственный способ, с помощью которого ваши пользователи могут ошибаться в объекте для чего-то изменяемого, заключается в том, чтобы сделать его явным, либо через имя самого объекта, либо через имя метода (как и с подробными параметрами типа "GetCopyWith" или "CopyAndAdd" ).

Так что просто идите со своим любимым, "Плюс".

Ответ 19

Во-первых, интересная отправная точка: http://en.wikipedia.org/wiki/Naming_conventions_(programming)... В частности, проверьте ссылки "См. также" внизу.

Я сторонник либо Плюс, либо И, эффективно одинаково.

Плюс и И оба они основаны на математике в этимологии. Как таковые, оба означают математическую операцию; оба дают выражение, которое естественно читается как выражения, которые могут быть преобразованы в значение, которое соответствует методу, имеющему возвращаемое значение. And имеет дополнительную логическую коннотацию, но оба слова интуитивно применяются к спискам. Add обозначает действие, выполняемое над объектом, которое конфликтует с неизменяемой семантикой метода.

Оба являются короткими, что особенно важно с учетом примитивности операции. Простые, часто выполняемые операции заслуживают более коротких имен.

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

Ответ 20

Как насчет Chain() или Attach()?

Ответ 21

Я предпочитаю Plus (и минус). Они легко понятны и непосредственно сопоставляются операциям с известными неизменяемыми типами (номерами). 2 + 2 не изменяет значение 2, оно возвращает новое, неизменяемое значение.

Некоторые другие возможности:

сращивания()

Прививка()

обрастать()

Ответ 22

По-видимому, я первый человек Obj-C/ Cocoa, чтобы ответить на этот вопрос.

NNString *empty = [[NSString alloc] init];
NSString *list1 = [empty stringByAppendingString:@"Hello"];
NSString *list2 = [list1 stringByAppendingString:@"immutable"];
NSString *list3 = [list2 stringByAppendingString:@"word"];

Не собирайтесь выигрывать в играх с кодами игры с этим.

Ответ 23

Как насчет mate, mateWith или coitus, для тех, кто соблюдает. С точки зрения воспроизводства млекопитающих обычно считаются неизменными.

Идет, чтобы бросить Союз. Заимствован из SQL.

Ответ 24

Я думаю, что "Добавить" или "Плюс" звучит нормально. Имя самого списка должно быть достаточно, чтобы передать неизменность списка.

Ответ 25

Возможно, есть несколько слов, которые помнят меня о создании копии и добавлении к ней материала вместо того, чтобы мутировать экземпляр (например, "Concatenate" ). Но я думаю, что иметь некоторую симметрию для этих слов для других действий было бы неплохо иметь тоже. Я не знаю аналогичного слова для "Удалить", что я думаю о том же, что и "Конкатенация". "Плюс" звучит немного странно для меня. Я бы не ожидал, что он будет использоваться в нелимитическом контексте. Но это могло произойти из моего неанглийского фона.

Возможно, я использовал бы эту схему

AddToCopy
RemoveFromCopy
InsertIntoCopy

У них есть свои проблемы, хотя, когда я думаю об этом. Можно было подумать, что они что-то убрали или что-то добавили к аргументу. Не уверен в этом вообще. Я думаю, что эти слова не очень приятны в цепочке. Слишком многословно.

Возможно, я просто использовал бы "Add" и друзей. Мне нравится, как он используется в математике

Add 1 to 2 and you get 3

Ну, конечно, 2 осталось 2, и вы получите новый номер. Это примерно два числа, а не список и элемент, но я думаю, что он имеет некоторую аналогию. По-моему, add не обязательно означает, что вы что-то мутируете. Я, конечно, вижу, что наличие одиночной инструкции, содержащей только add и не использующей возвращенный новый объект, не выглядит багги. Но я теперь тоже подумал об этой идее использовать другое имя, чем "добавить", но я просто не могу придумать другое имя, не заставляя меня думать "хм, мне нужно будет посмотреть документацию, чтобы узнать, что речь идет о" потому что его имя отличается от того, что я ожидаю назвать "добавлением". Просто некоторые странные мысли об этом от litb, не уверен, что это имеет смысл вообще:)

Ответ 27

Я думаю, что Plus() и Minus() или, альтернативно, Including(), Excluding() разумны, подразумевая неизменное поведение.

Тем не менее, ни один выбор именования никогда не станет понятным для всех, поэтому я лично считаю, что хороший комментарий к XML-документу будет очень долгим. VS бросает их прямо в лицо, когда вы пишете код в среде IDE - их трудно игнорировать.

Ответ 28

Append - потому что, обратите внимание, что имена методов System.String предполагают, что они мутируют экземпляр, но они этого не делают.

Или мне очень нравится AfterAppending:

void test()
{
  Bar bar = new Bar();
  List list = bar.AfterAppending("foo");
}

Ответ 29

list.CopyWith(element)

Как и Smalltalk:)

И также list.copyWithout(element), который удаляет все вхождения элемента, что наиболее полезно при использовании в качестве list.copyWithout(null) для удаления неустановленных элементов.

Ответ 30

Я бы пошел на Add, потому что я вижу преимущество лучшего имени, но проблема заключалась бы в том, чтобы найти разные имена для каждой другой неизменной операции, которая может сделать класс совершенно незнакомым, если это имеет смысл.