Haskell是一种托管语言吗?

15

我对Haskell完全是新手。其中一件让我困惑的事情是,Haskell是像Java那样的托管语言,还是像C那样编译成本地代码的语言?

GHC页面说:“GHC将Haskell代码直接编译成本地代码或使用LLVM作为后端。”

如果“编译成本地代码”,那么像垃圾回收这样的特性怎么可能没有像JVM那样的东西呢?

/更新/

非常感谢您的答案。从概念上讲,您能否帮助指出下面关于Haskell中垃圾回收的理解哪一个是正确的:

GHC将Haskell代码编译成本地代码。在编译过程中,垃圾回收例程将被添加到原始程序代码中?

还是说有一个程序与Haskell程序并行运行,来执行垃圾回收?


15
为什么需要使用虚拟机来进行垃圾回收? - sepp2k
4
当微软说“Managed”时,我非常确定他们指的不是Java,而是CLI托管语言,与“本机代码”不同。 - Arafangion
7
不想再赘述,但你可以对C语言进行垃圾回收(PDF链接)。 - Thomas M. DuBuisson
7
有一些问题。几乎任何语言都可以被编译成字节码或本机代码,如果你想的话,C#也可以被编译成本机代码。而普通的C语言可以被解释(存在着C语言解释器!很奇怪对吧!)。因此,这与语言本身无关,而完全取决于实现方式。这有点像问英语是否是一种本土语言——有些人是以英语为母语的,有些人则将英语作为外语学习。这与英语本身无关。 - Dietrich Epp
2
@TommyQ:这样看待它:如果程序员对所生成的确切本机代码具有如此多的控制权,那么编写一个良好的GC就很难(但并非不可能,因为已经证明过)。使用GHC,您(可能)对所生成的确切本机代码的控制要少得多,因此编译器可以自由添加任何需要实现良好GC的技巧,而无需虚拟机。区别仅在于,在.NET应用程序中,GC位于运行时和VM中,而在编译的Haskell代码中,它位于运行时和编译代码中。 - Joachim Sauer
显示剩余2条评论
5个回答

22
据我所知,“托管语言”这个术语特指针对.NET /公共语言运行时的语言。因此,不,Haskell不是托管语言,Java也不是。就Haskell编译成什么而言:正如您引用的文档所说,GHC将Haskell编译为本机代码。它可以通过直接发出本机代码或先发出LLVM代码然后让LLVM将其编译为本机代码来实现。无论哪种方式,运行GHC的最终结果都是本机可执行文件。除了GHC之外,还有其他Haskell实现方式,最值得注意的是Hugs,它是一个纯解释器,永远不会产生可执行文件(本地或其他)。
关于垃圾回收等功能如何在没有类似JVM的东西的情况下实现?和使用JVM一样:每次分配内存时,它都会向垃圾回收器注册。然后,垃圾回收器按照给定的垃圾回收算法步骤定期运行。GHC编译的代码使用分代垃圾回收。
关于编辑的回答:基本上是这样。但是,说“垃圾回收例程将添加到原始程序代码中”可能会形成错误的印象。 GC例程只是每个Haskell程序链接的库的一部分。编译的代码只是在适当的位置包含对这些例程的调用。它基本上只需要在每次调用malloc时调用GC的alloc函数即可。只需查看C中的任何GC库以及如何使用它:您所需做的就是#include库的标头并链接该库,将每个malloc出现替换为GC库的alloc函数(并删除所有free调用) ,你的代码就是垃圾回收的了。对于“是否有一个程序与Haskell程序并行运行以执行垃圾回收”这个问题,答案是否定的。

1
你所需做的就是#include库的头文件并链接到该库,然后将每个malloc出现的地方替换为GC库的alloc函数(并删除所有对free的调用),然后你的代码就可以进行垃圾回收了。非常感谢,这很清楚明了。 - TommyQ

13

关于Haskell是否像Java一样是受管理的语言,还是像C一样编译成原生代码的问题

GHC编译出来的程序包括垃圾回收器。(据我所知,所有的Haskell实现都包括垃圾回收,但这不是规范的一部分。)

GHC编译出来的程序被编译成原生代码。Hugs解释程序,而不是编译成原生代码。还有其他几个实现方式,据我所知,它们都会被编译成原生代码,但我将其单独列出,因为我对此事情不太确定。

如果是“编译成原生代码”,那么没有像JVM这样的东西,如何实现类似垃圾回收之类的功能呢?

GHC编译出来的程序包括运行时系统,提供了一些基本的能力,比如M对N的绿色线程、垃圾回收和IO管理器。在某种意义上,这有点像有一个“像JVM一样的东西”,因为它提供了许多相同的功能,但它的实现非常不同:没有跨所有架构的通用字节码(因此没有“虚拟机”)。

我的以下哪个理解关于Haskell中垃圾回收是正确的:

  1. GHC将Haskell代码编译成原生代码。在编译过程中,将垃圾回收例程添加到原始程序代码中。
  2. 有一个程序与Haskell程序一起运行来执行垃圾回收吗?

情况1是正确的:运行时系统代码在编译过程中被添加到程序代码中。


2
特定语言的运行时已经存在了一段时间。如果我没记错的话,Turbo Pascal就有一个。而且人们可以认为libc是“C特定的运行时”;-) - Joachim Sauer
1
@JoachimSauer:更好的是,在许多平台上都有C运行时。在Linux/ELF上,它包含对_start函数的定义,该函数调用main - Dietrich Epp
@JoachimSauer 当然可以;我并不打算暗示这是一个新想法! - Daniel Wagner
1
@DanielWagner:我知道,我只是想澄清一下,对于那些认为“语言运行时”意味着“虚拟机”的人。 - Joachim Sauer
@JoachimSauer:.NET公共语言运行时实际上是一台虚拟机。 :) - TommyQ
2
@TommyQ,这很好,但C语言运行时不是虚拟机,Turbo Pascal语言运行时也不是虚拟机,Haskell语言运行时也不是虚拟机... - Daniel Wagner

4

“Managed language”是一个含义重载的术语,以下是一些单词回答,然后是我通常想到的不同含义的细节:

作为CLR目标的托管

不是,Haskell不能编译成Microsoft CLI的IL。
好吧,我读到有一些解决方案可以做到,但在我看来,不要这样做... CLR并非为FP而建造,严重缺乏优化,可能会导致研究语言性能。如果我真的非常想针对CLR,我会使用F#——它不是函数式语言,但很接近。

N.B. 这是术语“托管语言”的最准确和实际含义。下一个含义是错误的,但仍然不幸地常见。

自动垃圾回收的托管

是的,这基本上是必须的。我的意思是,除了规范之外:如果我们需要垃圾回收,它将破坏使我们工作在我们心爱的家园高空的功能主题。

它还将强制执行不纯和内存模型。

编译成字节码,由VM运行的托管

不需要(通常)。
这取决于你的后端:不仅我们今天有不同的Haskell编译器,一些编译器还有不同的后端 -- 甚至有JavaScript后端!
因此,如果你想针对一个虚拟机,你可以使用现有的/制作一个后端。但是Haskell并不要求它。所以就像你可以编译成本地原始二进制文件一样,你也可以编译成其他任何东西。
与CLR语言(如C#1、VB.NET和Java等)相比,你不必将目标指向JVM、CLR、Mono等,因为Haskell根本不需要虚拟机。
GHC是一个很好的例子。当你在GHC中编译时,它不会直接编译成二进制代码,而是编译成一种叫做Core的中间语言,然后进行多次从Core到Core的优化,然后才转到另一种叫做STG的语言,最后才进行代码生成(如果你告诉它停在那里,它就会停止)。现在,你也可以使用它将代码编译为LLVM字节码(这是经过一些惊人的优化的)。使用LLVM后端,GHC可以产生非常快的程序。有关此信息和GHC后端的更多信息,请单击此处
下面的图示说明了GHC编译流程,您可以在此处找到有关各个阶段的更多信息。

The GHC compilation pipeline

看到底部的三个叉子了吗?那些就是我所提到的后端。


1未来的一个例外和有趣的事实:微软目前正在开发本地 .NET!它被巧妙地命名为:Microsoft .NET Native


1

对于您来说,“托管语言”的定义特征是什么?您引用的“GHC将Haskell代码直接编译为本地代码或使用LLVM作为后端”这句话非常清楚地说明了GHC的功能,因此我怀疑困扰您的“歧义”实际上是在术语“托管语言”而不是GHC的文档中。

在“编译为本地代码”的情况下,没有像JVM这样的东西,如何实现垃圾回收等功能呢?

您认为“像JVM这样的东西”是如何实现垃圾回收等功能的呢?JVM并不神奇,它只是像其他程序一样的程序。在某个层面上,您需要有本地代码才能让CPU执行它,因此垃圾回收等功能在本地代码中是可行的


是的,JVM 是一个执行 Java 字节码的程序。在 C 的情况下,CPU 直接执行编译后的代码,而不需要中间有虚拟机。我的问题是:Haskell 在编译后的代码和 CPU 之间没有执行任何东西的情况下如何进行垃圾回收? - TommyQ
1
同样的方式,VM也会这样做;代码被编译成程序,该程序跟踪内存并不时检查是否可以回收其中的任何内容(当然,在非常一般的层面上;在“跟踪内存”,“不时检查”和“回收”如何实现的细节方面有很大的可变性)。请记住,JVM本身是编译的本地代码;它能做的任何事情,其他编译的本地代码也可以做到。 - Ben

0

就目前情况而言,最好将 (GHC) Haskell 视为 "受管理的",但 GHC 编译的平台并没有被其他任何东西所针对。当然,这方面还有更多内容需要了解, 但对于缺乏 Haskell 经验的人来说,这已经是足够的解释了。


6
这完全是误导。在 Haskell 程序上运行 GHC 的结果是本机代码,而不是 C-- 或任何类型的虚拟机代码。无论 GHC 在内部使用什么样的表示方式,都几乎与最终结果无关。 - sepp2k
@sepp2k 但是你可以做一些事情,比如输出外部核心或LLVM IR,后者可以稍后解释(或JIT编译)。这就像MS C#编译器具有预先选项一样,只是GHC恰好使其成为默认选项。内部表示正是他所问的,因为这就是传统VM托管语言的工作方式:通过公开和持久化编译器的内部表示来实现。 - Ptharien's Flame

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接