在 luite 的帮助下,我终于通过为 JVM 添加一些shims使其正常运行:
Platform detection (shims/src/platform.js)
Java's Nashorn provides the global Java
variable which can be used to detect if we are running under JVM. If this variable is defined, a global variable h$isJvm
is set similar to h$isNode
for the ghcjs runtime. This variable will then be used to provide JVM specific code in other places. We can also define console.log
here so that writing to console works out of the box on the JVM without having to define it in the user program:
if(typeof Java !== 'undefined') {
h$isJvm = true;
this.console = {
log: function(s) {
java.lang.System.out.print(s);
}
};
}
Exiting JVM normally (shims/src/thread.js)
GHCJS has a method called h$exitProcess
which is used to exit the process. With the variable we defined in the previous step, h$isJvm
, we can add the following code for the JVM to exit:
if (h$isJvm) {
java.lang.System.exit(code);
}
Command line arguments (shims/src/environment.js)
Nashorn provides a global arguments
variable that contains the command line parameter values passed to jjs
. We can add a shim using this variable:
if(h$isJvm) {
h$programArgs = h$getGlobal(this).arguments;
}
使用这些“垫片”,我们可以在JVM上直接运行大多数Haskell代码。以下是在GHCJS中添加了上述“垫片”的原始程序:
module Main where
main = putStrLn "Hello from Haskell!"
这段普通的Haskell代码现在可以直接在JVM上运行。即使是一些稍微复杂的代码也可以直接在JVM上运行。例如,下面这段代码来自这里:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
import Options.Generic
data Example = Example { foo :: Int, bar :: Double }
deriving (Generic, Show)
instance ParseRecord Example
main = do
x <- getRecord "Test program"
print (x :: Example)
我们可以使用stack
构建它,并使用jjs
传递命令行参数运行:
haskell-jvm-hello$ stack build
haskell-jvm-hello$ jjs ./.stack-work/dist/x86_64-linux/Cabal-1.22.4.0_ghcjs/build/haskell-jvm-hello-exe/haskell-jvm-hello-exe.jsexe/all.js -- --help
Test program
Usage: a.js --foo INT --bar DOUBLE
Available options:
-h,--help Show this help text
haskell-jvm-hello$ jjs ./.stack-work/dist/x86_64-linux/Cabal-1.22.4.0_ghcjs/build/haskell-jvm-hello-exe/haskell-jvm-hello-exe.jsexe/all.js -- --foo 1 --bar 2.5
Example {foo = 1, bar = 2.5}