我正在研究纯粹的函数式语言,目前正在考虑一些不可变的数据实现。
这是伪代码。
List a = [1 .. 10000]
List b = NewListWithoutLastElement a
b
在评估b
时,必须在急需/严格执行不可变数据时复制b
。
但在这种情况下,a
不再在任何地方使用,因此可以安全地重复使用'a'的内存以避免复制成本。
此外,程序员可以强制编译器始终通过使用某些关键字含义List
标记类型must-be-disposed-after-using
来执行此操作。这使得逻辑上的编译时错误无法避免复制成本。
这可以获得巨大的成就。因为它也可以应用于巨大的对象图。
您怎么看?任何实现?
答案 0 :(得分:1)
这是可能的,但范围严重受限。请记住,功能程序中的绝大多数复杂值将传递给许多函数以从中提取各种属性 - 而且,大多数情况下,这些函数本身就是其他函数的参数,这意味着您无法做出任何假设关于他们。
例如:
let map2 f g x = f x, g x
let apply f =
let a = [1 .. 10000]
f a
// in another file :
apply (map2 NewListWithoutLastElement NewListWithoutFirstElement)
这在功能代码中是相当标准的,并且无法在must-be-disposed-after-using
上放置a
属性,因为没有特定位置对程序的其余部分有足够的了解。当然,您可以尝试将该信息添加到类型系统中,但对此类型推断显然是非平凡的(更不用说类型会变得非常大)。
当您拥有可能在值之间共享子元素的复合对象(例如树)时情况会变得更糟。考虑一下:
let a = binary_tree [ 1; 2; 5; 7; 9 ]
let result_1 = complex_computation_1 (insert a 6)
let result_2 = complex_computation_2 (remove a 5)
为了在complex_computation_2
中允许内存重用,您需要证明complex_computation_1
不会改变a
,不会将a
的任何部分存储在result_1
内1}}并在a
开始工作时使用complex_computation_2
完成。虽然这两个第一个要求可能看起来最难,但请记住,这是一个纯函数式语言:第三个要求实际上会导致大量性能下降,因为complex_computation_1
和complex_computation_2
不能再在不同的线程上运行!
实际上,这绝不是绝大多数功能语言的问题,原因有三:
NewListWithoutFirstElement
已经完全重用了转换列表的内存,而没有任何努力。功能程序员(以及任何类型的程序员,实际上)基于性能考虑来确定他们对数据结构的使用是相当普遍的,并且将“删除最后”算法重写为“先删除”算法很容易。b
读取元素将从a
中读取一个元素,确定它是否是最后一个元素,并在不需要存储的情况下返回它(可能是一个利弊单元)在那里被分配,但这种情况一直发生在函数式编程语言中,而短暂的小对象在GC中完全没问题。