是否有任何函数式编程语言具有用于更改对象的一部分的语法糖?

Do any functional programming languages have syntax sugar for changing part of an object?

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

foo.bar = new_value

或者指向数组的一个元素,或者在某些语言中指向类似数组的列表:

a[3] = new_value

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

在语义层面,这在理解和编写代码方面带来了显着改进,尽管并非没有权衡。

我在这里询问 语法 级别的权衡。一般来说,创建一个具有大部分相同值但某个字段或元素具有不同值的新对象,就其在代码中的外观而言是一项重量级得多的操作。

有没有函数式编程语言提供语法糖,让那个操作看起来更简洁?显然,您可以编写一个函数来完成它,但是命令式语言提供了语法糖,使其比调用过程更简洁;是否有任何函数式语言提供语法糖以使其比调用函数更简洁?我可以发誓我至少看到了 object.field 情况下的语法糖,在某种函数式语言中,尽管我忘记了它是哪一种。

(性能超出了这里的范围。在这种情况下,我只谈论代码的外观和功能,而不是它执行的速度。)

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

let myRecord3 = { myRecord2 with Y = 100; Z = 2 }

Scala 也有更新地图的功能:

ms + (k -> v)
ms updated (k,v)

在 Haskell 等语言中,您需要自己编写。如果可以将更新表示为 key-value 对,则可以定义

let structure' =
  update structure key value

update structure (key, value)

这会让你使用中缀符号,例如

structure `update` (key, value)
structure // (key, value)

作为概念证明,这是一种可能的(低效的)实现,如果您的索引超出范围,该实现也会失败:

module UpdateList (updateList, (//)) where
import Data.List (splitAt)

updateList :: [a] -> (Int,a) -> [a]
updateList xs (i,y) = let ( initial, (_:final) ) = splitAt i xs
  in initial ++ (y:final)

infixl 6 // -- Same precedence as +
(//) :: [a] -> (Int,a) -> [a]
(//) = updateList

有了这个定义,["a","b","c","d"] // (2,"C") returns ["a","b","C","d"][1,2] // (2,3) 抛出运行时异常,但我将其留作 reader.

的练习

H. Rhen 举了一个我不知道的 Haskell 记录语法的例子,所以我删除了答案的最后一部分。请参阅他们的。

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

data Person = Person
  { name :: String
  , age :: Int
  }

还有一个实例:

johnSmith :: Person
johnSmith = Person
  { name = "John Smith"
  , age = 24
  }

并创建一个交替:

johnDoe :: Person
johnDoe = johnSmith {name = "John Doe"}
-- Result:
-- johnDoe = Person
--   { name = "John Doe"
--   , age = 24
--   }

但是,当您必须更新深度嵌套的记录时,这种语法很麻烦。我们有一个库 lens 可以很好地解决这个问题。


但是,Haskell 列表不提供更新语法,因为更新列表的成本为 O(n) - 它们是 singly-linked 列表。

如果你想高效地更新list-like个集合,你可以在array包中使用Arrays,或者在vector包中使用Vectors。他们都有中缀运算符 (//) 用于更新:

alteredVector = someVector // [(1, "some value")]
-- similar to `someVector[1] = "some value"`

虽然不是built-in,但我觉得中缀符号很方便!