名称值/表达在功能程序中保存在哪里?

Nai*_*air 2 erlang haskell functional-programming scala

在C#中,所有值字段(如int,float)都保存在堆栈中,所有引用变量指针都在堆栈中,实际值保存在堆中.(希望我的理解在这里是正确的).
1.由于在函数式编程模型中没有值和引用类型,名称符号值保存在何处?
2.堆栈和堆如何在功能程序中发挥作用?
谢谢

Lui*_*las 17

您试图将C#(一种特定语言)与功能语言作为一组进行比较.这是一个苹果到橙子的比较(或者更确切地说,苹果与香料的比较?).

在命令式语言中,您可以观察到堆栈中存储的值与堆栈中存储的值之间的差异.例如,C和C++(据我所知)允许程序员手动选择他们想要的任何类型的这两种方式中的哪一种.

另一个微妙之处在于语言对程序员的保证与语言实现方式之间的区别.一个例子是Oracle Java VM的最新版本有一个称为"转义分析"的优化,如果VM可以证明对象引用没有逃脱该方法,则可以在堆栈上分配一个对象(在内联后确定是执行).因此,即使Java调用其对象类型"引用"类型,这并不意味着它将在堆中分配.引用Brian Goetz撰写的这篇文章:

Java语言没有提供任何在堆栈上显式分配对象的方法,但这一事实并不妨碍JVM在适当的地方仍然使用堆栈分配.JVM可以使用一种称为转义分析的技术,通过该技术,它们可以判断某些对象在其整个生命周期内仍然局限于单个线程,并且该生命周期受给定堆栈帧的生命周期的限制.可以在堆栈而不是堆上安全地分配这些对象.更好的是,对于小型对象,JVM可以完全优化分配,并简单地将对象的字段提升到寄存器中.

类似的考虑也适用于函数式语言 - 它完全取决于(a)语言的承诺,以及(b)语言实现的工作原理和复杂程度.但我们可以将功能语言世界划分为两个重要的阵营:

  1. 急切的功能语言,如Scheme,Scala,Clojure或ML.
  2. 像Haskell这样的惰性函数语言.

渴望语言有几种类型的实现:

  1. 纯堆栈实现.这些工作方式与现代命令式语言相同.Common Lisp以这种方式工作.由于JVM功能语言使用与Java相同的VM,因此它们也是如此.
  2. 纯粹的延续传递风格实现.这些是完全无堆栈的 - 包括激活帧在内的所有内容都在堆上分配.这些可以轻松支持尾调用优化一流的延续.我相信这种技术是由Scheme实现开创的,并且也被新泽西标准ML编译器使用.
  3. 混合实施.这些通常试图主要基于堆栈,但也支持尾调用优化,也许是一流的延续.示例:一堆随机Scheme系统.

懒惰语言是另一个故事,因为传统的调用堆栈实现并不直接转换为延迟评估. GHC Haskell编译器基于一个名为"STG Machine"的模型,它使用堆栈和堆,但STG堆栈的工作方式与命令式语言不同; STG堆栈中的条目不像传统堆栈条目那样对应于"函数调用".


dfl*_*str 5

由于函数式语言通常使用不可变值(意思是:您无法修改的"变量"),因此对于用户来说,值是存储在堆栈上还是存储在堆上并不重要.

因此,编译器通常会决定如何存储值.例如,它可能决定将小值(整数,浮点数,整数对,8字节数组等)存储在堆栈中,并将大值(字符串,列表,...)存储在堆上.这完全是编译器的决定.

对于像Haskell这样支持延迟评估的语言,需要将值存储在堆上(除非使用了一些技巧).这是因为变量需要是指向计算值的函数/闭包的指针,或指向实际已经计算的值的指针.