对不起,标题太小了-如果我能拿出一个简洁的标题,则不必提这个问题。
假设我有一个不变的列表类型。它具有一个操作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");
(这是C#代码,如果您觉得语言很重要,那么我对C#建议最感兴趣。从根本上讲,这不是语言问题,但语言的成语可能很重要。)
重要的是现有列表不会被更改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:不喜欢添加的原因。
各种答案都是有效的:“使用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>
:) 一样具有自我描述性: