Désolé pour le titre gaufré - 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 soient pas modifiées par Foo
- so empty.Count
retournerait 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 peut ne pas être réellement ImmutableList<T>
, ce qui rend la position claire. Imaginez plutôt que c'est TestSuite
et qu'il est immuable car l'ensemble du framework dont il fait partie est immuable ...
(Fin du montage 3)
Options que j'ai trouvées jusqu'à présent:
Add
: commun dans .NET, mais implique une mutation de la liste d'origineCons
: 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 langagesPlus
: mon préféré jusqu'à présent, cela n'implique pas de mutation pour moi. Apparemment, c'est aussi tilisé 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
: compatible avec certaines autres conventions immuables, mais n'a pas tout à fait la même "addition" à l'OMI.And
: peu descriptif.Les critères que j'utilise pour choisir sont:
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 cela 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 Plus
est mon préféré. 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 Ajouter.
Diverses réponses sont efficaces: "Allez avec Ajouter. C'est ce que fait DateTime
, et String
a Replace
des mé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 appeler DateTime.Add
ou String.Replace
et attendez une mutation. Il y a des tas de questions de newsgroup (et probablement SO celles si je creuse)) auxquelles "Vous ignorez la valeur de retour de String.Replace
; les chaînes sont immuables, une nouvelle chaîne est renvoyée. "
Maintenant, je devrais révéler une subtilité à la question - le type pourrait 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 un beaucoup plus trouble quand 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 mai ne sera pas possible, mais j'aimerais essayer.
Je m'excuse d'avoir simplifié à l'excès 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>
:)
Dans de telles situations, j'utilise généralement 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);
J'irais avec les inconvénients, pour une raison simple: cela signifie exactement ce que vous voulez.
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 qu'à long terme, c'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é.
Le fait que cela serait "vide de sens" pour les personnes sans expérience FP 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érent, soit ambigu. Un nouveau concept devrait avoir un nouveau mot (ou dans ce cas, un ancien). Je préférerais que quelqu'un regarde la définition de Cons, plutôt que de supposer à tort, il sait ce que fait Add.
D'autres opérations empruntées aux langages fonctionnels gardent souvent leurs noms d'origine, sans catastrophe apparente. Je n'ai vu aucun Push proposer des synonymes de "map" et "réduire" 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.)
En fait, j'aime 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");
Chaque fois que je suis dans un jam avec la nomenclature, je frappe les interwebs.
thesaurus.com renvoie ceci pour "ajouter":
Définition: adjacent, augmenter; faire d'autres commentaires
Synonymes: apposer, annexer, ante, ajouter, augmenter, booster, booster, construire, charger, continuer, cue in, figure in, chair sortir, chauffer, randonnée, randonnée, attelage, accrocher, brancher, inclure, jack up, jazz up, se réunir, pad, parlay, ferroutage, brancher, verser dessus, répondre, courir, dire plus loin, gifle, boule de neige, soupe, accélère, pointe, intensifie, supplément, adoucit, amure, étiquette
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 d'autres ImmutableList<>
.
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");
J'ai fini par utiliser Add pour toutes mes collections immuables dans BclExtras . La raison en est que c'est un nom facilement prévisible. 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é:
Options que j'ai envisagées:
Malheureusement, il ne semble pas vraiment y avoir un mot qui soit
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.
Modifier: Réponse à Plus Modifier
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 nouveau couplage, 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.
Édition 2
Conviens 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 à la place sur les 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
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.
Je pense que cela peut être l'une de ces rares situations où il est acceptable de surcharger le +
opérateur. En terminologie mathématique, nous savons que +
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 list2
n'est pas modifié dans la dernière ligne, mais plutôt que list3
se voit attribuer le résultat de l'ajout de "Word" à list2
.
Sinon, je nommerais simplement la fonction Plus ().
Pour être aussi clair que possible, vous voudrez peut-être utiliser le mot CopyAndAdd
ou quelque chose de similaire.
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.
J'aime la suggestion de mmyers de CopyAndAdd. Conformément à un thème de "mutation", vous pourriez peut-être choisir Bud (reproduction asexuée), Grow, Replicate, ou Évoluer? =)
EDIT: Pour continuer avec mon thème génétique, que diriez-vous Procréer, impliquant qu'un nouvel objet est créé qui est basé sur le précédent, mais avec quelque chose de nouveau ajouté.
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 dont vous avez besoin. Par exemple, une pile immuable a les méthodes "push" et "popped".
C'est probablement un tronçon, mais dans Ruby il y a une notation couramment utilisée pour la distinction: add
ne 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).
Join
semble approprié.
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!");
Copy
renverrait un objet intermédiaire, c'est une copie modifiable de la liste d'origine. With
renverrait une nouvelle liste immuable.
Mise à jour:
Mais, avoir une collection intermédiaire et mutable n'est pas une bonne approche. L'objet intermédiaire doit être contenu dans l'opération Copy
:
var list1 = new ImmutableList<string>("Hello");
var list2 = list1.Copy(list => list.Add("World!"));
Maintenant, l'opération Copy
prend un délégué, qui reçoit une liste mutable, 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 constructeur ImmutableList
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é d'interprétation erronée par les utilisateurs, ils tomber naturellement dans le gouffre du succès.
Encore une autre mise à jour:
Si vous n'aimez toujours pas la mention de liste mutable, même maintenant qu'elle est contenue, vous pouvez concevoir un objet de spécification, qui spécifiera , ou 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 que Copy
prenne 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 le rules.Append
la méthode renvoie 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())
);
Quelques réflexions aléatoires:
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.
Tout d'abord, un point de départ intéressant: http://en.wikipedia.org/wiki/Naming_conventions_ (programmation) ... 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, les deux connotent 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. And
comporte une connotation logique supplémentaire, mais les deux mots s'appliquent intuitivement aux listes. Add
connote 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.
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".
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 ().
Que diriez-vous de Chain () ou Attach ()?
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 ()
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.
Que diriez-vous mate, mateWith, ou coitus, pour ceux qui respectent. En termes de reproduction, les mammifères sont généralement considérés comme immuables.
Va jeter nion là-bas aussi. Emprunté à SQL.
Je pense que Plus()
et Minus()
ou, alternativement, Including()
, Excluding()
sont raisonnables à impliquant comportement immuable .
Cependant, aucun choix de nom ne le rendra parfaitement clair pour tout le monde, donc je crois 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 le IDE - ils sont difficiles à ignorer.
En regardant http://thesaurus.reference.com/browse/add et http://thesaurus.reference.com/browse/plus j'ai trouvé gain et affixe mais je ne sais pas dans quelle mesure ils impliquent une non-mutation.
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.
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 qu'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 "Remove" auquel je pense du même genre que "Concatenate". "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. Ces mots ne jouent pas non plus Nice dans l'enchaînement, je pense. 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, add
ne signifie pas nécessairement que vous mutez quelque chose. Je vois certainement votre argument que le fait d'avoir une déclaration solitaire contenant juste un add
et de ne pas utiliser le nouvel objet retourné ne semble pas bogué. Mais j'ai aussi réfléchi quelque temps à 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 :)
list.CopyWith(element)
Tout comme Smalltalk :)
Et aussi list.copyWithout(element)
qui supprime toutes les occurrences d'un élément, ce qui est très utile lorsqu'il est utilisé comme list.copyWithout(null)
pour supprimer les éléments non définis.
Append
- car, notez que les noms des System.String
les méthodes suggèrent de muter l'instance, mais ce n'est pas le cas.
Ou j'aime bien AfterAppending
:
void test()
{
Bar bar = new Bar();
List list = bar.AfterAppending("foo");
}
Personnellement, j'aime unite()
, car lorsque vous unissez des objets, vous conservez toujours leur individualité, mais leur union est une nouvelle entité distincte. Union
est similaire à ce qui est suggéré mais est déjà bien défini dans la théorie des ensembles et unite
est plus verby et me dit qu'en suivant la méthode j'ai une nouvelle entité. Je sais que c'est tard mais il n'a pas pu trouver la suggestion sur la liste. De plus, la Parole me rappelle les jours où les gens âgés et unis pour aller à la guerre .. hehe
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.
Peut-être qu'une méthode statique ou un opérateur (qui est statique) serait préférable. Cela détournerait la responsabilité des instances et les utilisateurs sauront immédiatement que l'opération n'appartient à aucune des instances. Il est particulièrement important de NE PAS utiliser de méthodes d'extension, car leur ressemblance avec les méthodes d'instance lors de l'utilisation va à l'encontre du but.
La méthode statique pourrait être appelée Join
:
public static class ListOperations {
public static ImmutableList<T> Join<T>(ImmutableList<T> list, T tail) {
// ...
}
public static ImmutableList<T> Join<T>(T head, ImmutableList<T> list) {
// ...
}
public static ImmutableList<T> Join<T>(ImmutableList<T> list1, ImmutableList<T> list2) {
// ...
}
// substitutes chaining:
public static ImmutableList<T> Join<T>(ImmutableList<T> list, params T[] tail) {
// ...
}
}
// ....
var list = new ImmutableList<string>("Hello");
var list2 = ListOperations.Join(list, "immutable"); // inferred type parameter
var list3 = ListOperations.Join(list2, "world!");
Mais je préférerais que C # ait fonctions sans classe ici. Ou au moins quelque chose comme la fonction d'importation statique de Java.
L'opérateur pourrait être +
:
var list = new ImmutableList<string>("Hello");
var list2 = list + "immutable";
var list3 = list2 + "world!";
Mais je préfère pouvoir utiliser autre chose comme <<
, ::
ou .
, qui ne sont pas possibles en C #.
De plus, les membres statiques semblent plus fonctionnels et je pense qu'ils se prêtent mieux à cette vue immuable.
Que diriez-vous de cloneWith
?
Raisons:
1. Pour les noms de mots uniques suggérés, je ne saurais pas exactement ce qu'ils font avant d'avoir vérifié la documentation.
2. Pour les noms composés suggérés jusqu'ici, j'aime mieux cloneWith
. Je saurais exactement ce que j'obtenais avec ce nom de méthode, il est relativement concis, il est facile de s'en souvenir et il "se sent" juste :)
3. Avec l'achèvement de code moderne dans la plupart des outils de programmation, les noms plus longs sont plus faciles à utiliser que jamais.
Bien que la concision ajoute souvent à la clarté, prenez la clarté sur la concision si vous devez choisir ...
En action:
var empty = new ImmutableList<string>();
var list1 = empty.cloneWith("Hello");
var list2 = list1.cloneWith("immutable");
var list3 = list2.cloneWith("Word");
J'irais avec list.NewList("Word")
car il décrit ce que vous faites (création d'une nouvelle liste).
Je dois convenir que cela n'implique pas que l'argument est ajouté à la fin de la liste, mais personnellement j'appartiens au camp succinct-si-possible, donc je favoriserais list.NewList("Word")
sur list.NewListAfterAppend("Word")
.
Le nom WithAdded se lit bien et est à mon humble avis meilleur qu'Ajouté qui peut être lu comme un adjectif.
var list2 = list1.WithAdded("immutable");
Ou pour une version un peu plus verbeuse:
var list2 = list1.WithAddedElement("immutable");
Je suis un peu en retard à la fête, mais je n'ai pas vu de réponse suggérant simplement de ne pas le faire du tout! Laissez-le rester immuable. N'importe quelle méthode que vous mettez va ouvrir à l'abus quand ils la mettent en boucle, ce qui arrivera finalement. Au lieu de cela, je conseillerais d'utiliser un constructeur à la place.
MyImmutableBuilder builder = new MyImmutableBuilder(someImmutable);
MyImmutable modifiedImmultable = builder.add(element1).add(element2).build();
Maintenant, dans le cas d'une ImmutableList, votre générateur serait vraiment juste une liste, très probablement.
Je suppose que tout se résume à ... vous voulez vraiment un moyen simple d'ajouter un seul élément à votre collection immuable? Si c'est une occurrence courante, vous ne voulez probablement pas utiliser un objet immuable (vous pouvez toujours appeler build()
chaque fois que vous voulez envoyer un instantané de l'objet dans son état actuel ...) Si ce n'est pas un phénomène courant, alors avoir un constructeur comme exigence pour le faire ne serait pas un obstacle important, surtout s'il était documenté que c'est ainsi que vous souhaitez les construire.
@supercat - considérez le scénario suivant que vous décrivez:
Stack a = someImmutableStackOfSize(10);
Stack b = a.popAndCopy(); // size 9
Stack c = a.popAndCopy(); // also size 9
// a b and c have the same backing list
Stack d = b.pushAndCopy("node1");
Stack e = c.pushAndCopy("node2");
// because d and e had the same backing list, how will it
// know that d's last element is node1, but e's last
// element is node2?
2 suggestions:
Une fonction "gratuite":
Foo f = new Foo(whatever);
Foo fPlusSomething = Foo.Concat(f, something);
Une surcharge constructeur (qui est en quelque sorte une variante du thème "fonction libre"):
Foo f = new Foo(whatever);
Foo fPlusSomething = new Foo(f, something);
J'irais personnellement avec AddVoid
ou l'inverse de VoidAdd
Si vous êtes un programmeur fonctionnel, cela a quelques noms:
(++)
prononcé "append". Ou cela peut être concat
, selon vos types:
-- join two things into one thing:
append :: a -> a -> a
-- join many things into one thing
concat :: [a] -> a
Ou vous pourriez vouloir dire (:)
, AKA cons
:
(:) :: a -> [a] -> [a]
si vous joignez des choses au début de la liste, snoc
si cela continue.
C'est du moins ce que nous appelons l'ajout de choses sur des listes à Haskell-land depuis 20 ans.
Remarque, ce n'est pas de l'arithmétique (+)
, car il est monoïde, pas un anneau.
copyWithSuffix:
serait un nom de méthode personnalisé similaire à la méthode de copie de chaîne immuable d'Objective-C stringByAppendingString:
.
var suite = new TestSuite<string>();
suite = suite.CopyWithSuffix("Str1")
.CopyWithSuffix("Str2")
.CopyWithSuffix("Str3");
Suffix
en communique l'addition et Copy
rend l'immuabilité claire. N'hésitez pas à remplacer Suffix
par quelque chose de plus logique pour le type de suite de tests qu'elle est réellement (peut-être que vous ne les suffixez pas à une seule grande chaîne interne, vous pourriez fournir des noms/paramètres de cas de test pour tous Je sais).
1) Analyse des questions
le résumé de la question peut être:
D'un immuable ... obtenir un nouveau immuable mais différent de l'original ..
Dans le contexte actuel de cette question:
À partir d'une liste immuable, obtenez une nouvelle liste immuable ayant une différence d'un nouvel élément en plus à la fin.
Pouvez-vous donner un "verbe" efficace ("méthode" en langage de programmation) qui exprime l'action de manière concise sans utiliser le simple verbe "Ajouter".
2) Analyse de proposition
Premier concept: Immuable donne un immuable similaire,
>> "copier" verbe exprimer la conservation dans le "contenu" mais moins pour cela des contraintes définies (immuables )
>> "clone" verbe exprime conservateur dans "contenu" mais aussi pour ses contraintes définies.
>> "twin" le verbe est similaire au clone mais n'est pas courant dans le logiciel, mais il est court et bon son.
Deuxième concept: le résultat donne quelque chose de différent de l'original
>> "ajouter" exprimer un action pour faire la différence entre l'original et le nouvel objet mais par définition nous n'agissons pas (muter) un immuable.
>> "plus" exprime le fait que le résultat du verbe sera augmenté sans perturber l'objet d'origine ... l'approche "adverbe" est plus "immuable" amicale
>> "avec" comme auparavant, il exprime le fait que "verbe" fera quelque chose de plus mais peut être ambigu quant à ce qu'il fera de plus. Peut également exprimer le fait que le "paramètre verbe" est le "plus" du "verbe"
) Réponses
Avec le verbe "clone"
>> augmentClone
>> growClone
>> extendClone
avec avec"
>> cloneWith
>> augmentCloneWith
>> growCloneWith
>> extendCloneWith
Avec le verbe "jumeau"
>> augmentTwin
>> growTwin
>> extendTwin
avec avec"
>> twinWith
>> augmentTwinWith
>> growTwinWith
>> extendTwinWith
Tout nom qui implique qu'un objet du même type sera retourné devrait être bien utilisé. Plus est un bon nom pour cela, comme si vous plus deux objets vous vous attendez à ce que le résultat soit retourné.
De plus, le son ne semble pas être le bon nom à utiliser dans ce cas, car vous ajoutez un test à une suite de tests.
GetWith () me semble être une option. Ou jamais GetTypeWith () où type est évidemment le type que vous utilisez. Ainsi, par exemple:
var list = new ImmutableList<String>();
var list2 = list.GetWith("First");
var list3 = list2.GetWith("Second");
// OR
var list2 = list.GetListWith("First");
Le Get implique que vous obtenez la liste qui est déjà contenue, et le With implique que vous voulez un autre objet avec lui. CopyWith () répondrait également à ces critères.
Le problème immédiat que je vois avec GetWith est qu'il n'est pas facilement devinable. Un développeur souhaite ajouter une suite, pas obtenir la suite actuelle. Je taperais immédiatement .Add et j'espère que l'intellisence a montré quelque chose de très proche de ce que j'attendais.
Bien que nommer la méthode correctement puisse réduire le risque d'utilisation abusive, s'il y avait une construction de langage qui a dit au compilateur: "Cette méthode ne fait que renvoyer un nouvel objet, elle doit être utilisée ou l'appel de méthode n'a pas de sens", puis le compilateur pourrait se plaindre d'une erreur/avertissement chaque fois que quelqu'un utilise la méthode dans une mauvaise configuration.
J'ai rempli un demande de fonctionnalité pour cela n'hésitez pas à voter si vous êtes d'accord.
En tant que programmeur c ++ et fan de la STL, j'ai proposé add_copy
. (cela suggérerait également remove_copy
, replace_copy
etc)
Cela semble se résumer à trouver un mot qui exprime que l'objet n'est pas modifié. En outre, le fait qu'il soit cloné et que l'élément transmis en tant que paramètre soit ajouté à la fin de la liste clonée. Les noms standard comme add
et clone
ne le couvriront jamais. Peut-être clone_and_append
Qui est également assez ambigu, car la partie append
peut impliquer que le paramètre est ajouté à la liste d'origine. Donc, cela devrait probablement être quelque chose comme clone_and_append_to_clone
Ou mieux encore append_to_clone
, Bien que celui-ci n'implique pas vraiment que le clone va être retourné par cette méthode, mais plutôt que le clone existe déjà en tant que fait partie de la liste des origines.
Donc, en réfléchissant davantage à la recherche d'un nom qui n'implique pas une modification de la liste d'origine et suggère également la création d'une nouvelle liste, tenez compte des éléments suivants:
var list1 = list.Offshoot("tail")
- au sens strict, ramification se réfère ici au nouvel élément et non toute la nouvelle liste. Un autre inconvénient apparent est qu'il ne s'agit pas d'une action, le nom le plus correct étant list.CreateOffshoot
. Cependant, je trouve que cela donne une indication claire du fait que la nouvelle liste contiendra la liste d'origine et que le nouvel élément viendra à la fin de la liste.var list1 = list.Spawn("tail")
- similaire à la précédente. L'avantage est qu'il s'agit d'une action, mais il y a une implication plus faible que la nouvelle liste contiendra la liste d'origine.var list1 = list.BranchOff("tail")
- une action qui suggère que la liste d'origine sera clonée. Une ambiguïté potentielle est qu'il n'est pas très clair comment l'élément de paramètre sera utilisé.Personnellement, j'appellerais la méthode Clone
et j'appellerais le paramètre AdditionalValue
car c'est essentiellement ce qu'il apparaît à faire et serait facilement compréhensible, par exemple.
var empty = new ImmutableList<string>();
var list1 = empty.Clone("Hello");
var list2 = list1.Clone("immutable");
var list3 = list2.Clone("Word");
Que diriez-vous de "Stick" ou "Stick To", il colle un élément à la fin.
Ou "Attacher" ou "Attacher à".
Que diriez-vous de créer une classe wrapper avec une méthode Augment
(ou AugmentWith
)?
Le problème avec des méthodes comme String.Replace
est que l'utilisateur peut penser à tort qu'il y a une mutation en cours car il peut ignorer la valeur de retour. Utilisez donc un paramètre out
pour la valeur "return". L'utilisateur ne peut pas ignorer que:
public class ImmutableList<T> {
public ImmutableList(params T[] items) {
// ...
}
/// <summary>
/// Creates a new list with the items in this list plus the supplied items.
/// </summary>
/// <param name="newList">
/// The new list created for the operation.
/// This is also the return value of this method.
/// </param>
/// <param name="items">Items to add to the new list.</param>
/// <returns>The new list.</returns>
public ImmutableList<T> Add(out ImmutableList<T> newList, params T[] items) {
// ...
}
}
Usage:
var list = new ImmutableList<string>("Hello", "Immutable");
ImmutableList<string> newList;
list.Add(out newList, "World");
J'irais avec la surcharge de l'opérateur +
. La raison en est que c'est ainsi que cela fonctionne dans Python - .append()
sur une liste mute la liste, alors que faire +
Avec une autre liste crée une nouvelle liste . +
N'implique pas non plus de mutation. Je pense que c'est pourquoi vous aimez tellement .Plus()
, donc si vous ne voulez pas surcharger l'opérateur, alors vous pouvez aller avec .Plus()
.
Je l'appellerais ToInclude
var empty = new ImmutableList<string>();
var list1 = empty.ToInclude("Hello");
var list2 = list1.ToInclude("immutable");
var list3 = list2.ToInclude("Word");
idiomatiquement (?)
var list = new ImmutableList<string>().ToInclude("Hello");
.ToInclude("immutable");
.ToInclude("Word");
Fonctionne également pour le cas que vous avez mentionné.
var list = new ImmutableList<string>();list.ToInclude("foo");
var suite = new TestSuite<string, int>();suite.ToInclude(x => x.Length);
Comme cela a été mentionné précédemment, vous essayez de faire 2 choses à la fois et de micro-optimiser juste à cette fin. Si la copie se produit en dehors de la définition de collection d'origine, des noms comme "Ajouter", "Ajouter" ne feront que confondre.
"CloneAppend" est ce que je pourrais utiliser dans cette situation. Quelque chose me dit que je ne serais pas dans cette situation. Je pense que vous allez bientôt avoir besoin d'autres types d'opérations similaires comme "CloneFirstN" ou "CloneRemoveLast". Et bientôt, vous vous rendrez compte qu'il est préférable de chaîner la méthode clone/copy, d'ajouter/supprimer tout ce dont vous avez besoin, puis de reconvertir la collection en immuable même si cela prend une ligne de code supplémentaire.
Je sais que cela bat un cheval apparemment mort, mais je pense que vous vous en approchez peut-être d'une manière étrange (d'où la difficulté de nommage). Quand j'ai un objet immuable, je ne m'attends pas souvent à ce que l'objet ait des fonctions qui impliquent une mutabilité (comme Add ou AddAll). Je sais que String & DateTime font cela, mais même j'ai été victime d'oublier d'utiliser les résultats des fonctions DateTime/String, donc j'éviterais ces pièges si possible.
Si j'étais vous, je suivrais le modèle Builder similaire à ce que Guava utilise. Voir collections immuables en goyave . Fondamentalement, le concept est que vous construisez l'objet immuable avec tout ce qu'il contiendra. Si vous souhaitez créer un nouvel objet immuable, vous devez utiliser un nouveau générateur.
var suite = new TestSuite.Builder<string, int>().add(newTest).build();
Vous pouvez développer le générateur pour permettre une variété de modifications à la suite de tests (en utilisant des conventions de dénomination de liste standard comme ajouter/supprimer si vous le souhaitez) avant que l'utilisateur appelle la génération.
J'utiliserais un constructeur.
Foo f1 = new Foo("one");
Foo f2 = new Foo(f1, "two");
Foo f3 = new Foo(f2, "three");
f1 contient "un". f2 contient "un", "deux". f3 contient "un", "deux", "trois".
Pour ce type de fonctions, j'utilise habituellement le verbe à la deuxième forme, Added
dans ce cas.
Cette convention est utilisée par Qt, par exemple QVector2D::normalized
renvoie un vecteur normalisé, tandis que QVector2D::normalize
normalise un vecteur.
De la même manière, Added
retournerait l'objet avec un nouvel élément ajouté.
Puisque le nom du type est ImmutableList spécifiant ainsi qu'il est en fait immuable, je pense que .Add () est très bien. Cependant, si vous êtes vraiment insistant sur autre chose, je pourrais aller avec quelque chose comme .AddEx () où Ex signifie étendu et implique que l'utilisateur devrait déterminer ce qu'est cet Ex (en lisant des documents) avant de l'utiliser. J'aime aussi la suggestion de Plus () et GetCopyWith ()
Que diriez-vous d'une méthode d'extension? Vous pouvez l'appeler Join
dans ce cas. Étant une méthode d'extension, les utilisateurs doivent savoir qu'il s'agit d'une méthode statique et peuvent donc leur donner une petite pause et les encourager à regarder la valeur de retour. Dans le même temps, vous avez la possibilité d'utiliser une méthode "instance".
public static ImmutableList<T> Join(this ImmutableList<T> body, T tail)
{
// add robust error checking in case either is null...
return new ImmutableList<T>(body, tail);
}
puis plus tard ...
var list = new ImmutableList<string>().Join("Hello")
.Join("Extensible")
.Join("World");
Je ne connais pas très bien le comportement accepté lors de l'affichage de plusieurs réponses, mais c'est une question intéressante car je pense que la nomenclature est une étape critique dans la conception et mon cerveau continue de réfléchir à celle-ci.
Le pseudo-code C # -ish suit:
interface Foo
{
// Constructors
Foo();
Foo(params Foo[] foos);
// Instance method
Foo Join(params Foo[] foos);
// Class method
static Foo Join(params Foo[] foos);
}
Vous pouvez donc appeler des choses comme ceci:
var f0 = new Foo();
var f1 = new Foo(new Foo(), new Foo(), new Foo());
var f2 = Foo.Join(new Foo(), new Foo(), new Foo());
var f3 = f0.Join(new Foo(), new Foo(), new Foo());
var f4 = new Foo(new Foo(new Foo()), new Foo(), new Foo(new Foo()));
Etc....
Très tard dans le jeu, mais que diriez-vous de Freeze
. Il y a priorité dans WPF pour utiliser Freeze
et IsFrozen
pour tester si un objet est modifiable. Certes, cela biaise un peu le sens dans la mesure où Freeze()
est généralement un moyen de rendre l'objet actuel immuable, mais s'il a un paramètre, vous pouvez voir que vous obtenez quelque chose qui est immuable .
var list = new ImmutableList<string>().Freeze("Hello")
.Freeze("Fridgid")
.Freeze("World");
Fondamentalement:
J'irais avec "CreateNewListWithAdditionalItems"
Cela ne me dérange pas les noms longs, mais celui-ci vous dit ce qu'il fera réellement. Je lèverais également une exception si vous utilisez Add ou toute autre méthode de modification.
Une réponse très tardive, mais je voulais ajouter mes deux cents: je pense que le point ici pourrait être le verbe plus que le verbe lui-même:
var output= myList.joinedWith("Element"); //or AddedTo, ConcatenatedTo...
Le participe change le sens, à mon avis: nous n'ajoutons rien à myList, mais retournons le résultat de l'opération.
J'arrive un peu tard ici, que diriez-vous de NewWith?
Personnellement, j'irais avec Pad (..) peut-être si vous recherchez un style fluide, également avec une méthode d'extension d'AndWith (..)
var list = new ImmutableList<string>().Pad("Hello")
.AndWith("immutable")
.AndWith("Word");
Emprunter de C, que diriez-vous Concat
J'aime et (). Je pense qu'il a le moins d'ambiguïté possible. Le seul choc auquel je peux penser est avec un logique Et, je ne vois pas que ce soit un problème avec un développeur C # et même pour VB je pense que le contexte rend peu probable de causer un problème et n'importe quel problème serait détecté rapidement au moment de la compilation. Cela fonctionne aussi bien en anglais "Faites quelque chose avec ceux-ci et cela" ou "Mettez ceux-ci et cela dans la boîte".
Je pense . Avec () est OK. Ma préoccupation est qu'elle peut ressembler un peu à une méthode linq Where <> surtout s'il y a un lambda comme argument. L'anglais dans ma tête est également moins clair, en particulier "Faites quelque chose à ceux-ci avec ça".
Je n'aime pas .Plus (). Je ne peux pas le dépasser comme synonyme de Add: plus = plus sign = + = add.
Donc je suppose qu'une méthode nommée "ImmutableAdd ()" est tout à fait trop simpliste?
.Trail implique une très bonne compréhension de la liste n'a pas changé, cet objet traîne derrière la liste, il n'y a pas été ajouté.
var list = new ImmutableList<string>().Trail("Hello");
.Trail("immutable");
.Trail("Word");
"Remplacer"? Il n'ajoute rien à la liste, il remplace la liste par une nouvelle.
J'irais pour le simple Add (). Une alternative serait Append (), si vous voulez indiquer qu'il s'agit vraiment d'une opération de collecte.
En plus de la méthode explicite, je suggérerais toujours d'implémenter l'operloaded + obverloaded. C'est une opération bien connue. Tout le monde sait que String est immuable, mais tout le monde utilise le "+" pour en créer de nouvelles instances.
"ajouter()"
Le fait qu'il renvoie une nouvelle liste est sans importance, c'est un détail d'implémentation révélé par la signature. L'action principale que la méthode accomplit est "l'ajout" d'un nouvel élément à une liste. Comment ou quoi il supprime ou renvoie ne doit pas faire partie du nom de la méthode. Si une méthode était synchronisée, cela affecterait-il le nom de la méthode - "synchronizedAdd ()" ??? - bien sûr que non.
Les classes comme String qui suivent le modèle potentiel de mutation ont toujours des noms de méthode très simples - aucun n'est un mot composé.
Puisque cette question est maintenant essentiellement un thésaurus: que diriez-vous de .Bring()
. Comme dans, donnez-moi cette liste et apportez cet élément avec elle?
Foo = List.Bring('short');
.Bring('longer');
.Bring('woah');
Ça ne roule pas, mais ça signifie ça, pour moi.
En fait, AndBring()
pourrait être encore mieux.
Que diriez-vous "Augmenter"?
C'est un mot différent d'Ajout, mais c'est un synonyme proche.
Un autre point en faveur de Plus
est le nouveau pour la plupart immuable Java time API , où plus()
est par exemple utilisé pour ajouter à Instant
s.
list.copyAndAppend (elt)
Comment ça sonne?)