如何最简单地构建一个在JVM上运行并生成Java字节码的F#编译器?

34

当前的F#编译器是用F#编写的,是开源的,并在.Net和Mono上运行,使其可以在许多平台上执行,包括Windows、Mac和Linux。 F#的代码引用机制已被用于在项目中将F#编译为JavaScript,例如WebSharperPitFunScript。 还有一些人对在JVM上运行F#代码很感兴趣。

我相信OCaml编译器的一个版本被用来最初引导F#编译器。

如果有人想构建一个在JVM上运行的F#编译器,那么哪种方式更容易:

  1. 修改现有的 F# 编译器以发出 Java 字节码,然后使用它编译 F# 编译器?
  2. 使用基于 JVM 的 ML 编译器,如 Yeti,在 JVM 上引导一个最小的 F# 编译器?
  3. 从头开始用 Java 重新编写 F# 编译器,因为 fjord 项目似乎正在尝试这样做?
  4. 其他方案?

值得注意的是,最近在Hacker NewsReddit以及该项目自己的Issues页面上有一些关于Fjord的讨论。 - Phillip Trelford
6个回答

12

另一个可能需要考虑的选项是将.NET CLR字节码转换为JVM字节码,就像http://www.ikvm.net使用JVM>CLR字节码一样。尽管fjord的所有者曾经考虑过并予以否决

选择1)中从高层获得支持,并让F#编译器团队具有可插拔的后端,可以发出Java字节码,在理论上似乎会产生最完美的解决方案。

但如果你看看其他被移植到不同平台的语言,情况很少如此。大多数时候都要从头开始重写。但这也可能是由于原始语言团队本身没有兴趣支持另类平台,原始主机实现可能无法支持多个后端,而且它已经过慢,不能作为可行的开始选项。

我的直觉是结合从头开始重写和能够尽可能地在原始实现中共享代码和自动化。例如,如果测试套件可以用于两种实现,那么这将极大地减轻JVM端的负担,并在确保语言平等性方面产生很大作用。


谢谢!我同意,一个可插拔的 F# 后端会提供最完美的解决方案。看起来Scala.Net在从Java到.Net的另一种方式时使用了IKVM作为“关键启用程序”。 Clojure-CLR似乎已经使用了C#和Clojure本身。我希望F#团队和开源社区之间的合作能够推出一个可插拔的F#后端,并且这将进而导致一流的JVM支持,虽然对许多任务来说,Java的类型提供程序可能已经足够支持。 - Phillip Trelford
1
@toyvo 对的,虽然在字节码级别上的方法仍然是一种选择,但我已经更新了答案,包括penberg关于这种方法的常见问题解答。 - mythz

12

如果我真的不得不这样做,我可能会从第一种方法开始 - 给现有编译器添加JVM后端。但我也会尝试争取一个不同的目标虚拟机。

引用并不是很相关 - 作为WebSharper的作者,我可以向您保证,虽然引用可以给您一个类似F#的漂亮语言进行编程,但它们是有限制的且不被优化。我想象对于潜在的JVM F#用户来说,门槛会更高 - 完全语言兼容性和可比性的性能。这非常困难。

以尾调用为例。在WebSharper中,我们应用启发式算法将某些本地尾调用优化为JavaScript中的循环,但这还不够 - 您无法像通用的F#库一样依赖TCO。对于WebSharper而言,这是可以接受的,因为我们的用户不希望完全拥有F#,但对于JVM F#移植而言,则不可行。我相信大多数JVM实现都不会执行TCO,因此必须通过一些间接方式来实现,这会引入性能损失。

@mythz提到的字节码重新编译方法非常吸引人,因为它不仅允许移植F#,而且理想情况下还允许将更多.NET软件移植到JVM。我在一个内部的WebSharper 3.0项目中使用了很多.NET字节码分析 - 我们正在探索编译.NET字节码而不是F#引用到JavaScript的选项。但那里面存在巨大的挑战:

  1. 很多BCL中的代码是不透明的(本地)- 您无法反汇编它

  2. 泛型模型相当复杂。我已经实现了一个JavaScript运行时,可以准确地模拟类和方法的泛型、实例化、类型生成和基本反射,并具有合理的性能。这在具有闭包的动态JavaScript中已经很困难了,而在JVM上以高性能完成可能也相当困难 - 但我可能只是没有看到简单的解决方案。

  3. 值类型在字节码中创建了重大的复杂性。我尚未为WebSharper 3.0解决这个问题。它们也不能被忽略,因为它们被许多您想要移植的库广泛使用。

  • 同样,基本反射在许多实际的.NET库中被使用,但在跨编译方面却是个噩梦,需要大量的本机代码以及对泛型和值类型的适当支持。

  • 此外,字节码方法并没有解决如何实现尾调用的问题。据我所知,Scala并没有实现尾调用。他们肯定有天赋和资金来做到这一点-他们不这样做,这告诉我JVM上实现TCO的实用性。对于我们的.NET->JavaScript端口,我可能会采用类似的方法-除非你专门要求跳板调用,否则不保证TCO的性能,这将导致性能损失一个或两个数量级。


    9

    有一个将OCaml编译成JVM的项目,OCaml-Java:它非常完整,特别是可以编译OCaml编写的编译器源代码。我不确定您对F#语言的哪些方面感兴趣,但如果您主要想获得一个成熟的严格类型的函数式语言,那么这可能是一个不错的选择。


    谢谢!OCaml-Java看起来很有意思,而且OCaml与F#非常相似。对于一些项目可能会有用。我想这也可能是启动F#的另一种途径。F#变为OCaml ,其中包括轻量语法活动模式类型提供程序会很有趣。 - Phillip Trelford

    8

    我认为无论哪种方法都需要付出大量的工作,但我认为你的第一个建议是唯一一个能避免引入许多附加的不兼容和错误的方法。编译器非常复杂,且在重载解析等方面存在许多特殊情况(规范可能也存在缺陷),因此新实现具有一致兼容的语义似乎非常不可能。


    谢谢!对我来说,F#编译器似乎很复杂(而且强大),所以为了兼容性,我同意修改现有编译器的源代码似乎是一个不错的选择。 - Phillip Trelford

    1
    修改现有的F#编译器以发出Java字节码,然后用它来编译F#编译器?使用基于JVM的ML编译器(如Yeti)在JVM上引导最小的F#编译器? 如果编译器是用F#编写的,那么移植编译器不应该很难。 我可能会选择第一种方式,因为这是唯一一种希望将新编译器与.net F#编译器保持同步的方法。 重写F#编译器作为fjord项目似乎是尝试的最不优雅的方法,依我之见。 还有其他什么吗? 当编译器完成时,你还有90%的工作要做。例如,不知道F#的情况,但我认为很容易使用任何.NET库。这意味着,基本问题是以某种方式移植.NET生态系统。

    这就是问题所在。移植当前的F#源代码需要在目标平台(JVM)上提供.NET类库。不得不将.NET BCL移植到JVM上是一个相当高的要求,特别是作为先决条件。 - Justin

    0

    我在寻找类似的东西,尽管更像是一个 F# 到 Akka 的翻译器/编译器。就 F# -> JVM 而言,我发现了两个不太成熟的选择:

      1. F# -> [Fjord][1] -> JVM.
    
      2. F# -> [Funscript][2] -> [Vert.X][3] -> JVM
    

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