Quel est le meilleur nom pour une méthode «ajouter» non mutante sur une collection immuable?

229

Désolé pour le titre maladroit - si je pouvais trouver un titre concis, je n'aurais pas à poser la question.

Supposons que j'ai un type de liste immuable. Il a une opération Foo(x)qui retourne une nouvelle liste immuable avec l'argument spécifié comme élément supplémentaire à la fin. Donc, pour construire une liste de chaînes avec les valeurs "Bonjour", "immuable", "monde", vous pouvez écrire:

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

(Il s'agit du code C #, et je suis plus intéressé par une suggestion C # si vous pensez que la langue est importante. Ce n'est pas fondamentalement une question de langue, mais les idiomes de la langue peuvent être importants.)

L'important est que les listes existantes ne soient pas modifiées par Foo- donc empty.Countretournerait toujours 0.

Une autre façon (plus idiomatique) d'arriver au résultat final serait:

var list = new ImmutableList<string>().Foo("Hello")
                                      .Foo("immutable")
                                      .Foo("word");

Ma question est: quel est le meilleur nom pour Foo?

EDIT 3 : Comme je le révèle plus tard, le nom du type pourrait ne pas être réellement ImmutableList<T>, ce qui rend la position claire. Imaginez plutôt que c'est TestSuiteet qu'il est immuable car l'ensemble du cadre dont il fait partie est immuable ...

(Fin du montage 3)

Options que j'ai trouvées jusqu'à présent:

  • Add: commun en .NET, mais implique une mutation de la liste d'origine
  • Cons: Je crois que c'est le nom normal dans les langages fonctionnels, mais sans signification pour ceux qui n'ont pas d'expérience dans de tels langages
  • Plus: mon préféré jusqu'à présent, cela n'implique pas de mutation pour moi . Apparemment, cela est également utilisé dans Haskell mais avec des attentes légèrement différentes (un programmeur Haskell pourrait s'attendre à ce qu'il ajoute deux listes ensemble plutôt que d'ajouter une seule valeur à l'autre liste).
  • With: cohérent avec d'autres conventions immuables, mais n'a pas tout à fait la même "addition" à l'OMI.
  • And: peu descriptif.
  • Surcharge de l'opérateur pour +: je n'aime vraiment pas beaucoup ça; Je pense généralement que les opérateurs ne devraient être appliqués qu'aux types de niveau inférieur. Mais je suis persuadé!

Les critères que j'utilise pour choisir sont:

  • Donne l'impression correcte du résultat de l'appel de méthode (c'est-à-dire qu'il s'agit de la liste d'origine avec un élément supplémentaire)
  • Indique aussi clairement que possible qu'il ne mute pas la liste existante
  • Cela semble raisonnable lorsqu'il est enchaîné comme dans le deuxième exemple ci-dessus

Veuillez demander plus de détails si je ne me fais pas assez clair ...

EDIT 1: Voici mon raisonnement pour préférer Plusà Add. Considérez ces deux lignes de code:

list.Add(foo);
list.Plus(foo);

À mon avis (et c'est une chose personnelle), ce dernier est clairement buggé - c'est comme écrire "x + 5;" comme une déclaration à part entière. La première ligne semble correcte, jusqu'à ce que vous vous souveniez qu'elle est immuable. En fait, la façon dont l'opérateur plus seul ne mute pas ses opérandes est une autre raison pour laquelle Plusje préfère. Sans la légère lourdeur de la surcharge des opérateurs, cela donne toujours les mêmes connotations, qui incluent (pour moi) de ne pas muter les opérandes (ou la méthode cible dans ce cas).

EDIT 2: Raisons de ne pas aimer Add.

Diverses réponses sont effectivement: "Allez avec Add. C'est ce qui DateTimefait, et Stringa des Replaceméthodes, etc. qui ne rendent pas l'immuabilité évidente." Je suis d'accord - il y a priorité ici. Cependant, j'ai vu beaucoup de gens appellent DateTime.Addou String.Replaceet attendent une mutation . Il y a des tas de questions de newsgroup (et probablement SO si je creuse) auxquelles répond "Vous ignorez la valeur de retour de String.Replace; les chaînes sont immuables, une nouvelle chaîne est retournée."

Maintenant, je devrais révéler une subtilité à la question - le type peut ne pas être en fait une liste immuable, mais un type immuable différent. En particulier, je travaille sur un cadre d'analyse comparative où vous ajoutez des tests à une suite, et qui crée une nouvelle suite. Il pourrait être évident que:

var list = new ImmutableList<string>();
list.Add("foo");

ne va rien accomplir, mais cela devient beaucoup plus trouble lorsque vous le changez en:

var suite = new TestSuite<string, int>();
suite.Add(x => x.Length);

On dirait que ça devrait aller. Alors que cela, pour moi, rend l'erreur plus claire:

var suite = new TestSuite<string, int>();
suite.Plus(x => x.Length);

Cela ne demande qu'à être:

var suite = new TestSuite<string, int>().Plus(x => x.Length);

Idéalement, j'aimerais que mes utilisateurs n'aient pas à être informés que la suite de tests est immuable. Je veux qu'ils tombent dans le gouffre du succès. Ce n'est peut-être pas possible, mais j'aimerais essayer.

Je m'excuse d'avoir trop simplifié la question d'origine en ne parlant que d'un type de liste immuable. Toutes les collections ne sont pas aussi auto-descriptives que ImmutableList<T>:)

Jon Skeet
la source
2
@Adam: Non, ce dernier est clairement buggé. Ils sont tous deux en fait buggés (car ils ne font rien avec le résultat) - mais le premier ne me semble pas buggé.
Jon Skeet
36
Un non chat est-il un chien?
Michael Myers
7
Concat / Condog ... fonctionne pour moi! ;)
gnovice
11
Uncat - ce serait un chat zombie.
Erik Forbes
2
@Trap: Cela craint cependant de rendre l'API fluide.
Jon Skeet

Réponses:

130

Dans des situations comme ça, j'y vais habituellement Concat. Cela m'implique généralement qu'un nouvel objet est en cours de création.

var p = listA.Concat(listB);
var k = listA.Concat(item);
MojoFilter
la source
3
C'est ce que j'utilise, car c'est ce que System.Linq.Enumerable utilise - la décision est déjà prise. :) Je ne peux pas être la seule personne à avoir défini sa propre surcharge de l'extension Concat sur IEnumerable pour accepter une seule valeur à ajouter.
Daniel Earwicker
9
+1 Ceci est la bonne réponse pour maintenir la cohérence des noms dans le cadre.
Sam Harwell
Sauf si je le manque, System.Linq.Enumerable (et Linq en général) n'utilise Concat () que pour "séquence + séquence", jamais "élément + séquence". Le problème que quelqu'un "pourrait s'attendre à ce qu'il ajoute deux listes ensemble plutôt que d'ajouter une seule valeur à l'autre liste" a été explicitement invoqué comme raison de ne pas utiliser l'une des autres options d'origine.
Ssswift
119

J'irais avec les inconvénients, pour une raison simple: cela signifie exactement ce que vous voulez.

  1. Je suis un grand fan de dire exactement ce que je veux dire, en particulier dans le code source. Un débutant devra rechercher la définition des inconvénients une seule fois, puis la lire et l'utiliser mille fois. Je trouve que, à long terme, il est plus agréable de travailler avec des systèmes qui facilitent le cas commun, même si le coût initial est un peu plus élevé.

  2. Le fait que ce serait «insignifiant» pour les personnes sans expérience en PF est en fait un gros avantage. Comme vous l'avez souligné, tous les autres mots que vous avez trouvés ont déjà une signification, et cette signification est soit légèrement différente, soit ambiguë. Un nouveau concept doit avoir un nouveau mot (ou dans ce cas, un ancien). Je préfère que quelqu'un regarde la définition de Contre, plutôt que de supposer à tort qu'il sait ce que fait Add.

  3. D'autres opérations empruntées aux langages fonctionnels gardent souvent leurs noms d'origine, sans catastrophe apparente. Je n'ai vu aucun effort pour trouver des synonymes de «carte» et de «réduction» qui semblent plus familiers aux non-FPers, et je ne vois aucun avantage à le faire.

(Divulgation complète: je suis un programmeur Lisp, donc je sais déjà ce que signifie Cons.)

Ken
la source
16
N'oubliez pas que la découvrabilité est importante - si je tape "testSuite". et regardez la liste des méthodes, je voudrais voir une méthode qui suggère la bonne chose. Je ne chercherai probablement pas un nom absurde (pour moi) au hasard, c'est ce que je veux.
Jon Skeet
33
Si je me sentais snarky, je ferais une méthode Add qui lançait simplement une exception avec message = "Use Cons to prepend to an ImmutableList". :-)
Ken
2
Cela ne tuera personne de lire quelques lignes de commentaires expliquant pourquoi le nom est ce qu'il est et les référant, par exemple, à "Structure et interprétation des programmes informatiques" d'Abelson & Sussman.
John R. Strohm
16
Traditionnellement, les contre ajoutent au début plutôt qu'à la fin. C'est donc un nom potentiellement trompeur pour la méthode décrite.
walkytalky
16
Juste pour enregistrer le prochain programmeur non fonctionnel un clic ou 3 ... en.wikipedia.org/wiki/Cons du mot "construire", l'expression "contre x sur y" signifie construire un nouvel objet avec ( contre xy)
Myster
59

En fait j'aime bien And, surtout de façon idiomatique. J'aimerais particulièrement que vous ayez une propriété statique en lecture seule pour la liste vide, et peut-être rendre le constructeur privé afin que vous deviez toujours construire à partir de la liste vide.

var list = ImmutableList<string>.Empty.And("Hello")
                                      .And("Immutable")
                                      .And("Word");
tvanfosson
la source
J'aime l'idée vide. Mais toujours pas convaincu. C'est juste un peu bla.
Jon Skeet
Cela ressemble plus à la façon dont je penserais à construire une liste de choses en langage naturel. Si vous le lisez à haute voix, cela semble plus intuitif que Add ou Plus. "liste" est la liste vide et "Bonjour" et "Immuable" et "Word". Je conviens cependant que c'est moins clair de manière isolée.
tvanfosson
52

Chaque fois que je suis dans un jam avec la nomenclature, je frappe les interwebs.

thesaurus.com renvoie ceci pour "ajouter":

Définition: attenant, augmenter; faire d'autres commentaires

Synonymes: apposer, annexer, ante, ajouter, augmenter, booster, booster, accumuler, charger, continuer, cue in, figure in, chair out, heat up, hike, hike up, hitch on, hook on, hook up avec, incluez, jack up, jazz up, réunissez-vous, pad, parlay, ferroutage, branchez-le, versez-le, répondez, courez, dites plus loin, slap on, boule de neige, soupe, accélérez, pointez, intensifiez, supplément, édulcorer, coller, étiqueter

J'aime le son de Adjoin, ou plus simplement Join. C'est ce que tu fais, non? La méthode pourrait également s'appliquer pour rejoindre les autres ImmutableList<>.

spoulson
la source
1
J'aime un peu "Join" aussi mais dans la plupart des cas, lorsque vous joignez 2 objets, vous vous retrouvez avec 1. Dans ce cas, si vous joignez 2 objets, vous créez en fait un nouvel objet.
Outlaw Programmer
1
Je ne connais aucun cas en .NET, Perl, PHP ou même VBScript où Join implique une mutation. La conception est telle que A et B se rejoignent pour former C, où C est toujours une nouvelle entité.
spoulson
J'aime Join, et je suis totalement d'accord avec thesaurus.com :) Utilisez-le tout le temps en cas de doute sur un nom.
Skurmedel
var suite = new TestSuite <string, int> () .Join (x => x.Length);
Sly Gryphon
3
Allez avec Piggyback, imo. Ou HookUpWith.
Chris Marasti-Georg
48

Personnellement, j'aime .With (). Si j'utilisais l'objet, après avoir lu la documentation ou les commentaires du code, il serait clair ce qu'il fait, et il se lit bien dans le code source.

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

Ou, vous ajoutez "Parallèlement" à cela .. :)

object.AlongWith("this new item");
LarryF
la source
+1. "WITH" est un opérateur infixe dans SETL qui fait la même chose. Si SETL l'utilise, cela doit être correct :-)
finnw
1
Bah ... Qui utilise plus VB? :) Euh .. Opps .. C'était à haute voix? heh .. Mais, non, très sérieusement, c'est pourquoi j'ai considéré le "AlongWith", qui supprimerait le problème VB. Il y a seulement environ un million de façons différentes dont il pourrait aller avec celui-ci ... Je veux dire, même des fous comme: object.Plus (), ou Object.ExistingPlus () ... etc ... C'est une sacrément bonne question qu'il posté, cependant ... heh ..
LarryF
33

J'ai fini par utiliser Add pour toutes mes collections immuables dans BclExtras . La raison en est que c'est un nom facile à prévoir. Je ne m'inquiète pas tant que les gens confondent Add avec un add mutant puisque le nom du type est préfixé par Immutable.

Pendant un certain temps, j'ai considéré les inconvénients et d'autres noms de style fonctionnel. Finalement, je les ai réduits parce qu'ils ne sont pas aussi bien connus. Bien sûr, les programmeurs fonctionnels comprendront, mais ils ne sont pas la majorité des utilisateurs.

Autres noms: vous avez mentionné:

  • Le plus: je fais des vœux / je lave sur celui-ci. Pour moi, cela ne le distingue pas non plus comme une opération non mutante
  • Avec: causera des problèmes avec VB (jeu de mots voulu)
  • Surcharge de l'opérateur: la découvrabilité serait un problème

Options que j'ai envisagées:

  • Concat: Les chaînes sont immuables et utilisent ceci. Malheureusement, c'est seulement vraiment bon pour ajouter à la fin
  • CopyAdd: Copiez quoi? La source, la liste?
  • AddToNewList: Peut-être un bon pour List. Mais qu'en est-il d'une collection, d'une pile, d'une file d'attente, etc ...

Malheureusement, il ne semble pas vraiment y avoir un mot qui soit

  1. Certainement une opération immuable
  2. Compréhensible pour la majorité des utilisateurs
  3. Représentable en moins de 4 mots

Cela devient encore plus étrange lorsque vous envisagez des collections autres que List. Prenez par exemple Stack. Même les programmeurs de première année peuvent vous dire que les piles ont une paire de méthodes Push / Pop. Si vous créez un ImmutableStack et lui donnez un nom complètement différent, appelons-le Foo / Fop, vous venez d'ajouter plus de travail pour qu'ils utilisent votre collection.

Edit: Réponse à Plus Edit

Je vois où tu vas avec Plus. Je pense qu'un cas plus solide serait en fait Minus à supprimer. Si je voyais ce qui suit, je me demanderais certainement à quoi pensait le programmeur

list.Minus(obj);

Le plus gros problème que j'ai avec Plus / Minus ou un nouvel appairage, c'est qu'il semble exagéré. La collection elle-même a déjà un nom distinctif, le préfixe immuable. Pourquoi aller plus loin en ajoutant du vocabulaire dont l'intention est d'ajouter la même distinction que le préfixe immuable l'a déjà fait.

Je peux voir l'argument du site d'appel. Cela le rend plus clair du point de vue d'une seule expression. Mais dans le contexte de l'ensemble de la fonction, cela semble inutile.

Modifier 2

Convenez que les gens ont définitivement été confondus par String.Concat et DateTime.Add. J'ai vu plusieurs programmeurs très brillants rencontrer ce problème.

Cependant, je pense que ImmutableList est un argument différent. Il n'y a rien sur String ou DateTime qui l'établit comme Immuable pour un programmeur. Vous devez simplement savoir qu'il est immuable via une autre source. La confusion n'est donc pas inattendue.

ImmutableList n'a pas ce problème car le nom définit son comportement. On pourrait dire que les gens ne savent pas ce qu'est Immutable et je pense que c'est également valable. Je ne le savais certainement pas avant la deuxième année au collège. Mais vous avez le même problème avec le nom que vous choisissez au lieu de Ajouter.

Edit 3: Qu'en est-il des types comme TestSuite qui sont immuables mais ne contiennent pas le mot?

Je pense que cela renvoie à l'idée que vous ne devriez pas inventer de nouveaux noms de méthode. À savoir parce qu'il y a clairement une volonté de rendre les types immuables afin de faciliter les opérations parallèles. Si vous vous concentrez sur la modification du nom des méthodes pour les collections, l'étape suivante sera le nom des méthodes de mutation sur chaque type que vous utilisez qui est immuable.

Je pense que ce serait un effort plus précieux de se concentrer plutôt sur la création de types identifiables comme immuables. De cette façon, vous pouvez résoudre le problème sans repenser chaque modèle de méthode de mutation.

Maintenant, comment pouvez-vous identifier TestSuite comme immuable? Dans l'environnement actuel, je pense qu'il y a plusieurs façons

  1. Préfixe avec Immutable: ImmutableTestSuite
  2. Ajoutez un attribut qui décrit le niveau d'immutabilité. C'est certainement moins découvrable
  3. Pas grand-chose d'autre.

Je suppose / espère que les outils de développement commenceront à résoudre ce problème en facilitant l'identification des types immuables simplement par la vue (couleur différente, police plus forte, etc.). Mais je pense que c'est la réponse cependant en changeant tous les noms de méthode.

JaredPar
la source
J'ai ajouté des raisons à ma préférence pour Plus par rapport à Ajouter à la question. Accueillerait avec plaisir les commentaires sur ce raisonnement. Je suis vraiment ouvert à l'idée que je suis stupide.
Jon Skeet
@JaredPar: Et si le type s'appelle TestSuite?
Jon Skeet
J'adorerais donner un autre +1 pour l'édition 3, mais évidemment pas. (Dans mon cas, je n'essaie pas d'obtenir le parallélisme - je crois que l'immuabilité conduit à du code qui est plus facile à raisonner.) Je ne suis toujours pas tout à fait convaincu que Add est la voie à suivre, mais le soutien des réponses ici est convaincant.
Jon Skeet
Si vous pouviez déposer cette question dans une conversation au travail, ce serait génial aussi, btw. J'ai commencé la discussion sur une liste C #, donc certains collègues peuvent déjà être impliqués. Je pourrais aussi demander en interne à Google ...
Jon Skeet
@Jon, je pense que je connais un bon endroit pour poser cette question au travail :)
JaredPar
27

Je pense que cela peut être l'une de ces rares situations où il est acceptable de surcharger l' +opérateur. En terminologie mathématique, nous savons que cela +n'ajoute pas quelque chose à la fin de quelque chose d'autre. Il combine toujours deux valeurs ensemble et renvoie une nouvelle valeur résultante.

Par exemple, il est intuitivement évident que lorsque vous dites

x = 2 + 2;

la valeur résultante de x est 4, pas 22.

De même,

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

devrait indiquer clairement ce que chaque variable va contenir. Il doit être clair que cela list2n'est pas modifié dans la dernière ligne, mais à la place, list3le résultat de l'ajout de "mot" à list2.

Sinon, je nommerais simplement la fonction Plus ().

Bill le lézard
la source
Et si vous avez une liste générique à laquelle vous pouvez légitimement ajouter un entier (disons) ou une autre liste d'entiers? Ensuite, cette utilisation de + entrera en conflit avec la concaténation.
finnw
@finnw: Je ne vois pas votre point. Avec une liste, je m'attendrais toujours à + pour signifier ajouter, que vous ajoutiez un élément ou plusieurs.
Bill the Lizard
4
Pour une API réutilisable, c'est une bonne idée d'avoir également une méthode nommée, au cas où quelqu'un utilise votre classe à partir d'un langage sans surcharge d'opérateur.
Neil
"que vous ajoutiez un ou plusieurs éléments" - que faire si vous souhaitez ajouter la liste en tant qu'élément unique? c'est à dire, [1] + 2 = [1,2]mais [1] + [2] = [1,[2]]. Ce que vous proposez, c'est un comportement incohérent. C'est probablement pourquoi Python ne vous permet pas d'ajouter un élément de cette façon.
mpen
Eh bien, dans un langage typé comme C #, vous pouvez dire quel est le type d'éléments de liste, qu'il s'agisse d'une liste de listes ou d'une liste d'éléments, vous pouvez donc utiliser le type pour décider d'ajouter un élément qui est une liste ou de concaténer les deux listes.
Dobes Vandermeer
22

Pour être aussi clair que possible, vous voudrez peut-être aller avec le plus verbeux CopyAndAddou quelque chose de similaire.

Michael Myers
la source
Cependant, toutes les collections immuables ne nécessitent pas de copie. Pensez aux arbres immuables, ils ont juste besoin d'un nouveau nœud et peuvent utiliser l'arbre existant comme feuille.
JaredPar
BTW, j'ai lu votre profil, puis j'ai vieilli dans l'ordre et j'ai été brièvement étonné de votre âge. Sanity a fait son entrée peu de temps après.
JaredPar
Premier commentaire: j'utilise les arbres si rarement que cette pensée ne m'est jamais venue à l'esprit. C'est un bon point.
Michael Myers
Re deuxième commentaire: je l'avoue, je l'ai fait pour le badge! Je n'aime simplement pas révéler mon âge. (Je suis en fait plus jeune que vous, mais je peux assez bien jouer avec le vieux crotchety si je le dois. Et je ne suis pas le seul dont l'âge est de 89 ans.)
Michael Myers
L'argument des arbres immuables est fort, bon point. Ainsi, EquivalentReferenceWithAdded (x) combattrait cet argument, mais cela semble stupide et difficile à déduire.
TheBlastOne
21

Je l'appellerais Extend () ou peut-être ExtendWith () si vous vous sentez vraiment bavard.

Étendre signifie ajouter quelque chose à quelque chose d'autre sans le changer. Je pense que c'est une terminologie très pertinente en C # car elle est similaire au concept des méthodes d'extension - elles "ajoutent" une nouvelle méthode à une classe sans "toucher" la classe elle-même.

Sinon, si vous voulez vraiment souligner que vous ne modifiez pas du tout l'objet d'origine, utiliser un préfixe comme Get- me semble inévitable.

DrJokepu
la source
1
Encore un peu implique de faire quelque chose à l'objet de base.
chaos
18

Ajouté (), ajouté ()

J'aime utiliser le passé pour des opérations sur des objets immuables. Il transmet l'idée que vous ne changez pas l'objet d'origine, et il est facile à reconnaître lorsque vous le voyez.

De plus, comme les noms de méthode de mutation sont souvent des verbes au présent, cela s'applique à la plupart des cas de nom de méthode immuable que vous rencontrez. Par exemple, une pile immuable a les méthodes "push" et "popped".

Strilanc
la source
1
Python utilise également cette convention. Par exemple, les fonctions intégrées sont inversées () et triées ().
Joe
18

J'aime la suggestion de mmyers de CopyAndAdd . Conformément à un thème de «mutation», vous pourriez peut-être opter pour Bud (reproduction asexuée), Grow , Replicate ou Evolve ? =)

EDIT: Pour continuer avec mon thème génétique, que diriez-vous de Procréer , impliquant qu'un nouvel objet est créé qui est basé sur le précédent, mais avec quelque chose de nouveau ajouté.

gnovice
la source
14

C'est probablement un tronçon, mais dans Ruby il y a une notation couramment utilisée pour la distinction: addne mute pas; add!mute. Si c'est un problème omniprésent dans votre projet, vous pouvez le faire aussi (pas nécessairement avec des caractères non alphabétiques, mais en utilisant systématiquement une notation pour indiquer les méthodes de mutation / non-mutation).

ykaganovich
la source
+1. ce n'est pas imposé par la langue, mais convenu en tant que convention. J'aime ça.
oma
13

Join semble approprié.

ykaganovich
la source
13

La confusion vient peut-être du fait que vous voulez deux opérations en une. Pourquoi ne pas les séparer? Style DSL:

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

Copyrenverrait un objet intermédiaire, c'est une copie modifiable de la liste d'origine. Withretournerait une nouvelle liste immuable.

Mettre à jour:

Mais, avoir une collection intermédiaire et mutable n'est pas une bonne approche. L'objet intermédiaire doit être contenu dans l' Copyopération:

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

Maintenant, l' Copyopération prend un délégué, qui reçoit une liste modifiable, afin qu'il puisse contrôler le résultat de la copie. Cela peut faire bien plus que l'ajout d'un élément, comme la suppression d'éléments ou le tri de la liste. Il peut également être utilisé dans le ImmutableListconstructeur pour assembler la liste initiale sans listes immuables intermédiaires.

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);
}

Maintenant, il n'y a aucune possibilité de mauvaise interprétation par les utilisateurs, ils tomberont naturellement dans le gouffre du succès .

Encore une autre mise à jour:

Si vous n'aimez toujours pas la mention de liste modifiable, même maintenant qu'elle est contenue, vous pouvez concevoir un objet de spécification, qui spécifiera , ou un script , comment l'opération de copie transformera sa liste. L'utilisation sera la même:

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!"));

Vous pouvez maintenant être créatif avec les noms de règles et vous ne pouvez exposer que les fonctionnalités que vous souhaitez Copyprendre en charge, et non toutes les capacités d'un IList.

Pour l'utilisation du chaînage, vous pouvez créer un constructeur raisonnable (qui n'utilisera pas le chaînage, bien sûr):

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

...

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

Ou utilisez le même délégué dans un autre constructeur:

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

Cela suppose que la rules.Appendméthode retourne this.

Voici à quoi cela ressemblerait avec votre dernier exemple:

var suite = new TestSuite<string, int>(x => x.Length);
var otherSuite = suite.Copy(rules => 
  rules
    .Append(x => Int32.Parse(x))
    .Append(x => x.GetHashCode())
);
Jordão
la source
@Jordao: Parce que ce type de composition conduit à une forme d'initalisation beaucoup plus simple. Pourquoi avoir deux variables distinctes alors que je n'en veux qu'une? De même, je ne veux pas créer une copie mutable - je veux que tout soit immuable partout, car cela conduit à du code plus facile à raisonner, IMO.
Jon Skeet
Tous ces éléments sont encore plus compliqués que "Plus", etc. Merci pour les idées impliquant une mutation, mais je préfère définitivement l'approche "garder immuable".
Jon Skeet
Peut-être alors y aura-t-il toujours des interprétations erronées sur la véritable sémantique de l'opération. Si vous suivez la route de l'objet de spécification, aucune mutation n'est en cours (du moins pas en externe), l'objet spécifie uniquement à quoi ressemblera le nouvel objet immuable. Ce que vous avez, c'est une opération qui va d'un objet immuable à un autre, et comme elle s'appelle Copier, il n'y a pas de place pour les malentendus.
Jordão
Les seuls noms qui fonctionneraient pour vous semblent être des noms composés, comme CopyAndAppend, CopyAndAdd, etc.
Jordão
1
En fait, je suis de plus en plus friand de cette approche (avec toutes les modifications) en tant qu'API pour les collections immuables. Cependant, je dirais que combiner cette approche avec des méthodes d' assistance pour les spécifications par défaut, telles que Concat, offre le meilleur des deux mondes.
chanvre
11

Quelques réflexions aléatoires:

  • ImmutableAdd ()
  • Ajouter()
  • ImmutableList <T> (ImmutableList <T> originalList, T newItem) Constructeur
Chris Shaffer
la source
1
+1 pour ImmutableAdd; pas passionné par Append (trop comme StringBuilder.Append qui mute) et la version constructeur est pénible en termes de chaînage.
Jon Skeet
10

DateTime en C # utilise Add. Alors pourquoi ne pas utiliser le même nom? Tant que les utilisateurs de votre classe comprennent que la classe est immuable.

Tundey
la source
Je dirais que DateTime.Add est connu pour confondre les gens ... mais je suis d'accord qu'il montre la priorité.
Jon Skeet
1
Tout comme les méthodes pour muter une chaîne sont source de confusion pour les nouveaux développeurs. Mais bientôt tout le monde apprend que "les cordes sont immuables"
Tundey
9

Je pense que l'élément clé que vous essayez d'obtenir, qui est difficile à exprimer, est la non-permutation, donc peut-être quelque chose avec un mot génératif, quelque chose comme CopyWith () ou InstancePlus ().

le chaos
la source
9

Je ne pense pas que la langue anglaise vous laissera impliquer l'immuabilité d'une manière indubitable tout en utilisant un verbe qui signifie la même chose que "Ajouter". "Plus" le fait presque, mais les gens peuvent toujours faire l'erreur.

La seule façon d'empêcher vos utilisateurs de confondre l'objet avec quelque chose de mutable est de le rendre explicite, soit par le nom de l'objet lui-même, soit par le nom de la méthode (comme avec les options détaillées comme "GetCopyWith" ou "CopyAndAdd").

Alors, allez-y avec votre favori, "Plus".

Adam Bellaire
la source
9

Tout d'abord, un point de départ intéressant: http://en.wikipedia.org/wiki/Naming_conventions_(programming) ... En particulier, consultez les liens "Voir aussi" en bas.

Je suis en faveur de Plus ou Et, de manière égale.

Plus et And sont tous deux basés sur les mathématiques en étymologie. En tant que tels, tous deux évoquent une opération mathématique; les deux produisent une expression qui se lit naturellement comme des expressions qui peuvent se résoudre en une valeur, ce qui correspond à la méthode ayant une valeur de retour. Andporte une connotation logique supplémentaire, mais les deux mots s'appliquent intuitivement aux listes. Addconnote une action effectuée sur un objet, qui entre en conflit avec la sémantique immuable de la méthode.

Les deux sont courts, ce qui est particulièrement important étant donné la primitivité de l'opération. Les opérations simples et fréquemment exécutées méritent des noms plus courts.

Exprimer une sémantique immuable est quelque chose que je préfère faire via le contexte. Autrement dit, j'impliquerais simplement que tout ce bloc de code a une sensation fonctionnelle; supposons que tout est immuable. Mais c'est peut-être juste moi. Je préfère l'immuabilité à la règle; si c'est fait, c'est beaucoup fait au même endroit; la mutabilité est l'exception.

Paul Brinkley
la source
8

Que diriez-vous de Chain () ou Attach ()?

Webjedi
la source
1
Attacher sonne sûrement comme une mutation. Peut-être que WithAttached serait plus sage, mais est très proche de WithAdded, qui a été discuté ci-dessus.
TheBlastOne
C'est drôle combien de fois le "chaînage" est mentionné dans la description de ce qu'on essaie de faire avec ça. (5 fois), mais pas comme suggestion! C'est la première chose qui est apparue visualthesaurus.com(sans affiliation) lorsque j'ai cherché concatenate.
cod3monk3y
7

Je préfère Plus (et Moins). Ils sont facilement compréhensibles et correspondent directement aux opérations impliquant des types immuables bien connus (les nombres). 2 + 2 ne change pas la valeur de 2, il renvoie une nouvelle valeur, également immuable.

Quelques autres possibilités:

Épissure()

Greffer()

Accrete ()

2 tours
la source
6

Que diriez-vous de mate , mateWith ou coït , pour ceux qui demeurent. En termes de reproduction, les mammifères sont généralement considérés comme immuables.

Va jeter Union là-bas aussi. Emprunté à SQL.

jason saldo
la source
2
+1 pour Union. Je l'emprunte cependant directement à la théorie des ensembles. :-)
Christoffer Lette
SQL ne tire-t-il pas une grande partie de sa terminologie de la théorie des ensembles?
Jason D
1
De plus, Union implique une distinction. Si vous souhaitez inclure des doublons (au moins dans TSQL), vous devez utiliser Union Tout explicitement.
chanvre
6

Apparemment, je suis le premier Obj-C / Cocoa à répondre à cette question.

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

Je ne gagnerai aucun jeu de golf avec ce code.

kubi
la source
+1 le [object]By[Verb]ing[Object]:préfixe de la méthode implique qu'une nouvelle chaîne sera retournée par opposition à simplement [verb][Object]:, ce qui muterait l'objet en place.
Dave DeLong
3
+1. Je ne comprends pas l'aversion des gens à la verbosité (aka auto-documentation).
hatfinch
5

Je pense que "Ajouter" ou "Plus" sonne bien. Le nom de la liste elle-même devrait être suffisant pour exprimer l'immuabilité de la liste.

David Morton
la source
Ce n'est pas une liste, c'est une suite de tests.
tstenner
5

Peut-être qu'il y a des mots qui me rappellent plus de faire une copie et d'ajouter des choses à cela au lieu de muter l'instance (comme "Concaténer"). Mais je pense que d'avoir une certaine symétrie pour ces mots pour d'autres actions serait bien d'avoir aussi. Je ne connais pas de mot similaire pour "Supprimer" auquel je pense du même genre que "Concaténer". "Plus" me semble peu étrange. Je ne m'attendrais pas à ce qu'il soit utilisé dans un contexte non numérique. Mais cela pourrait aussi provenir de mes antécédents non anglais.

Peut-être que j'utiliserais ce schéma

AddToCopy
RemoveFromCopy
InsertIntoCopy

Cependant, ceux-ci ont leurs propres problèmes, quand j'y pense. On pourrait penser qu'ils retirent quelque chose ou ajoutent quelque chose à un argument donné. Pas sûr du tout. Je pense que ces mots ne jouent pas bien dans l'enchaînement. Trop verbeux pour taper.

Peut-être que j'utiliserais simplement "Ajouter" et mes amis aussi. J'aime la façon dont il est utilisé en mathématiques

Add 1 to 2 and you get 3

Eh bien, certainement, un 2 reste un 2 et vous obtenez un nouveau numéro. Il s'agit de deux nombres et non d'une liste et d'un élément, mais je pense qu'il a une certaine analogie. À mon avis, addcela ne signifie pas nécessairement que vous mutez quelque chose. Je vois certainement votre point que le fait d'avoir une déclaration solitaire contenant juste un addet de ne pas utiliser le nouvel objet renvoyé ne semble pas bogué. Mais j'ai maintenant réfléchi à l'idée d'utiliser un autre nom que "ajouter" mais je ne peux pas trouver un autre nom sans me faire penser "hmm, il faudrait que je regarde la documentation pour savoir quoi il s'agit de "parce que son nom diffère de ce que j'attendrais d'être appelé" ajouter ". Juste une réflexion bizarre de la part de litb, pas sûr que cela ait du sens :)

Johannes Schaub - litb
la source
Mais une copie n'est pas toujours nécessaire dans une collection immuable. Prenez par exemple un arbre binaire. L'ajout d'une nouvelle racine ne nécessite aucune copie, juste une nouvelle valeur où l'une des feuilles est le vieil arbre
JaredPar
KISS - Si le nommage commençait à inclure des détails d'implémentation, tous les noms de méthode deviendraient trop longs. "ajouter" est assez simple et fait l'affaire.
mP.
1
@mP: Encore une fois, vous utilisez l'expression "détail d'implémentation" d'une manière qui, selon moi, ne convient pas. Qu'il s'agisse d'une liste chaînée ou d'un tableau sous le capot est un détail d' implémentation . Je pourrais changer l' implémentation sans changer l'API. L'aspect immuable n'est pas un détail d'implémentation.
Jon Skeet
1
droite. JaredPar, mais je pense qu'il n'est pas important non plus qu'il copie réellement l'arbre ou utilise simplement l'arbre existant et l'utilise comme feuille pour le nouvel arbre renvoyé. je veux dire, c'est juste un détail de mise en œuvre. c'est la même chose pour (je crois) l'opération de sous-chaîne pour la classe de chaîne de java.
Johannes Schaub - litb
5

Je pense que Plus()et Minus()ou, alternativement Including(), Excluding()sont raisonnables pour impliquer un comportement immuable.

Cependant, aucun choix de nom ne le rendra parfaitement clair pour tout le monde, donc je pense personnellement qu'un bon commentaire de doc xml irait très loin ici. VS les jette directement dans votre visage lorsque vous écrivez du code dans l'EDI - ils sont difficiles à ignorer.

LBushkin
la source
4

Append- car, notez que les noms des System.Stringméthodes suggèrent qu'ils mutent l'instance, mais ils ne le font pas.

Ou j'aime bien AfterAppending:

void test()
{
  Bar bar = new Bar();
  List list = bar.AfterAppending("foo");
}
ChrisW
la source
Comme l'argument DateTime plus tôt, je serais d'accord avec la priorité s'il n'y avait pas le fait que tant de programmeurs s'attendent à ce que les méthodes de chaîne fassent des choses. Il faut leur dire (parfois à plusieurs reprises) que les chaînes sont immuables. Je voudrais attirer mes développeurs clients dans le gouffre du succès :)
Jon Skeet
J'aime de plus en plus. Il distingue que vous ne modifiez pas la collection mais que vous vous y joignez.
4

list.CopyWith(element)

Tout comme Smalltalk :)

Et list.copyWithout(element)cela supprime également toutes les occurrences d'un élément, ce qui est très utile lorsqu'il est utilisé list.copyWithout(null)pour supprimer des éléments non définis.

akuhn
la source
3

Je choisirais Add, car je peux voir l'avantage d'un meilleur nom, mais le problème serait de trouver des noms différents pour chaque autre opération immuable qui pourrait rendre la classe assez peu familière si cela a du sens.

Joan Venge
la source
Oui, je vois votre point. Je pense que je préfère toujours Plus, mais c'est un point qui mérite réflexion.
Jon Skeet
Merci Jon. J'ai hâte d'utiliser votre framework. Je viens de lire à ce sujet et ça a l'air très bien. Merci encore.
Joan Venge