Syntax 任何函数式编程语言都有语法糖来更改对象的一部分吗?

Syntax 任何函数式编程语言都有语法糖来更改对象的一部分吗?,syntax,functional-programming,language-design,ml,Syntax,Functional Programming,Language Design,Ml,在命令式编程中,有一种简洁的语法,用于更改对象的一部分,例如分配给字段: foo.bar = new_value 或数组的元素,或在某些语言中为类似数组的列表: a[3] = new_value 在函数式编程中,习惯用法不是改变现有对象的一部分,而是创建一个具有大多数相同值的新对象,但该字段或元素的值不同 在语义层面上,这在理解和编写代码的易用性方面带来了显著的改进,尽管并非没有权衡 我在这里问的是语法层面的权衡。一般来说,创建一个具有大多数相同值的新对象,但一个字段或元素的值不同,就其在代

在命令式编程中,有一种简洁的语法,用于更改对象的一部分,例如分配给字段:

foo.bar = new_value
或数组的元素,或在某些语言中为类似数组的列表:

a[3] = new_value
在函数式编程中,习惯用法不是改变现有对象的一部分,而是创建一个具有大多数相同值的新对象,但该字段或元素的值不同

在语义层面上,这在理解和编写代码的易用性方面带来了显著的改进,尽管并非没有权衡

我在这里问的是语法层面的权衡。一般来说,创建一个具有大多数相同值的新对象,但一个字段或元素的值不同,就其在代码中的外观而言,这是一个更为繁重的操作

有没有函数式编程语言提供语法糖,使操作看起来更简洁?显然,您可以编写一个函数来实现它,但命令式语言提供了语法糖,使其比调用过程更简洁;有没有函数语言提供语法糖,使其比调用函数更简洁?我可以发誓,我至少在一些函数式语言中见过object.field的语法sugar,尽管我忘了它是哪一种


(性能超出了本文的范围。在本文中,我只讨论代码的外观和功能,而不是它的运行速度。)

一种有这种糖分的语言是F。它允许你写作

让myRecord3={myRecord2,Y=100;Z=2}
Scala还有更新地图的功能:

ms+(k->v)
ms更新(k,v)
用Haskell这样的语言,你需要自己写这个。如果可以将更新表示为键值对,则可以定义

let structure'=
更新结构键值

更新结构(键、值)
这将允许您使用中缀符号,例如

structure`update`(键、值)
结构//(键、值)
作为概念证明,这里有一个可能的(低效的)实现,如果索引超出范围,它也会失败:

模块更新列表(更新列表,(//)),其中
导入数据列表(splitAt)
更新列表::[a]->(Int,a)->[a]
updateList xs(i,y)=let(initial,(:final))=splitAt i xs
初始++(y:最终)
infixl 6/--与相同的优先级+
(/)::[a]->(Int,a)->[a]
(/)=更新列表
使用此定义,
[“a”、“b”、“c”、“d”]/(2,“c”)
返回
[“a”、“b”、“c”、“d”]
。而
[1,2]/(2,3)
抛出一个运行时异常,但我把它留给读者作为练习


H.Rhen给出了一个我不知道的Haskell记录语法示例,因此我删除了答案的最后一部分。请参见他们的记录。

Haskell记录具有此功能。您可以将记录定义为:

data Person=Person
{name::String
,年龄::Int
}
还有一个例子:

johnSmith::Person
约翰史密斯=人
{name=“约翰·史密斯”
,年龄=24岁
}
并创建一个备选方案:

johnDoe::Person
johnDoe=johnSmith{name=“johnDoe”}
--结果:
--约翰多=人
--{name=“约翰·多伊”
--,年龄=24岁
--   }
但是,当您必须更新深度嵌套的记录时,这种语法很麻烦。我们有一个库
lens
,可以很好地解决这个问题


但是,Haskell列表不提供更新语法,因为在列表上更新会有O(n)代价-它们是单链接列表

如果希望对列表类集合进行高效更新,可以在数组包中使用
Array
s,或在向量包中使用
Vector
s。它们都有中缀运算符
(//)
用于更新:

alteredVector=someVector/[(1,“somevalue”)]
--类似于'someVector[1]=“some value”`

它不是内置的,但我认为中缀符号已经足够方便了

首先,如果你使用功能光学(透镜等),你可以以一种非常简洁的方式执行惊人的复杂更新,而不仅仅是像
o.prop=…
这样的简单情况。但是你特别要求语法糖。除了有更通用的方法之外,没有理由不提供特殊的getter/setter语法。下面是一个如何在purescrip中更新记录的示例
r1
是旧记录,以下部分是记录文字,不需要包含所有的
r1
属性:
r2=r1{x=42,y=“foo”}
与Haskell的链表相比,
(//)
操作符效率高的答案是不正确的
(//)
复制向量,这意味着更新将是
O(n)
——与链表相同。这是因为默认向量是不可变的。像这样的单一更新需要的是可变向量。