Currently there are 2 ways we can write Haskell on the JVM:
Frege, a language that follows Haskell 2010 standard and compiles to Java.
Haskell itself by compiling it to JavaScript via GHCJS.
Frege is basically a Haskell for the JVM and infact conforms to Haskell 2010 with few inbuilt GHC extensions. Even with good Java interop, it doesn’t sacrifice its type guarantees and currently is the only pure language on the JVM.
In this post, I am going to explore another interesting option: Haskell itself on the JVM. Haskell can be compiled to JavaScript using GHCJS and Java has an inbuilt JavaScript engine, called Nashorn so it is actually possible to compile Haskell to JavaScript and run the resulting JavaScript on the JVM.
Here is a simple Haskell code that can be run on the JVM:
Nashorn doesn’t have an inbuilt console object and Haskell’s putStrLn prints to the console so we have to provide an implementation of console. The implementation, as can be seen from the code above, is actually backed by Java’s System.out.print. That is our first example of calling Java from Haskell. sysexit is another function calling Java. sysexit is needed here as otherwise the program just keeps on running which I think is because of JavaScript event loop or something similar that prevents the JVM from shutting down.
Compiling Haskell with GHCJS and running on JVM
123456
$ ghcjs -o HelloJava HelloJava.hs
[1 of 1] Compiling Main ( HelloJava.hs, HelloJava.js_o )
Linking HelloJava.jsexe (Main)
$ jjs HelloJava.jsexe/all.js
Hello from Haskell!
jjs is a JVM laucher for JavaScript code similar to Node. It is also possible to run this as a regular Java program along with other Java classes without jjs. jjs is just a convenient way to run just JavaScript code on the JVM. Above GHCJS compiles the Haskell code to JavaScript in one file all.js and the JVM runs the JavaScript code from all.js.
Example 2
Now let’s look at another example that shows how to convert between Haskell and Java lists:
Converstion between Haskell and Java Lists (HaskellJavaListsConversion.hs)download
{-# LANGUAGE ForeignFunctionInterface #-}{-# LANGUAGE JavaScriptFFI #-}{-# LANGUAGE UnliftedFFITypes #-}{-# LANGUAGE GHCForeignImportPrim #-}{-# LANGUAGE TypeSynonymInstances #-}{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE DataKinds #-}{-# LANGUAGE PolyKinds #-}{-# LANGUAGE DeriveDataTypeable #-}{-# LANGUAGE TypeFamilies #-}moduleMainwhereimportControl.Monad.STimportGHCJS.TypesimportGHCJS.ForeignimportGHCJS.PrimimportData.TypeableimportGHC.STdataMutabilityTypes=Mutable|Immutable|STMutablesdataIsItMutable=IsImmutable|IsMutable-- Copied from GHCJS.Internal.Types. Not sure why this is not exposed.typefamilyMutability(a::MutabilityTypes)::IsItMutablewhereMutabilityImmutable=IsImmutableMutabilityMutable=IsMutableMutability(STMutables)=IsMutable{- java.util.ArrayList class and its methods -}newtypeSomeArrayList(a::MutabilityTypes)=SomeArrayListJSValderivingTypeabletypeArrayList=SomeArrayListImmutabletypeMutableArrayList=SomeArrayListMutabletypeSTArrayLists=SomeArrayList(STMutables)instanceIsJSVal(SomeArrayListm)-- ArrayList Constructorforeignimportjavascriptunsafe"new $1()"arrayList_new::JType->STs(STArrayLists)-- Adds an element to ArrayListforeignimportjavascriptunsafe"$2.add($1)"arrayList_add::JSVal->STArrayLists->STs(){- java.util.Iterator class and its methods -}newtypeSomeIterator(a::MutabilityTypes)=SomeIteratorJSValderivingTypeabletypeIterator=SomeIteratorImmutabletypeMutableIterator=SomeIteratorMutabletypeSTIterators=SomeIterator(STMutables)instanceIsJSVal(SomeIteratorm)-- Create an Iterator from an ArrayListforeignimportjavascriptunsafe"$1.iterator()"iterator::STArrayLists->STs(STIterators)foreignimportjavascriptunsafe"$1.hasNext()"iterator_hasNext::STIterators->STsBoolforeignimportjavascriptunsafe"$1.next()"iterator_next::STIterators->STsJSVal{- Other Nashorn imports -}-- Represents a Java typenewtypeJType=JTypeJSValforeignimportjavascriptunsafe"java.lang.System.out.println($1)"jprintln::JSVal->IO()foreignimportjavascriptunsafe"java.lang.System.exit($1)"sysexit::Int->IO()-- Imports a Java classforeignimportjavascriptunsafe"Java.type($1)"jimport::JSVal->JType{- Create an instance of Java's ArrayList from Haskell's list -}listToArrayList::[JSVal]->STs(STArrayLists)listToArrayListxs=doletarrayListClass=jimport$toJSString"java.util.ArrayList"arrList<-arrayList_newarrayListClassgoxsarrListwherego[]arrList=returnarrListgo(x:xs)arrList=doarrayList_addxarrListgoxsarrList{- Create Haskell's list from Java's Iterator -}iteratorToList::STIterators->STs[JSVal]iteratorToListitr=reverse<$>go[]wheregoacc=dohasNext<-iterator_hasNextitrifhasNextthendonext<-iterator_nextitrgo(next:acc)elsereturnacc-- Nashorn doesn't provide default console object. Haskell's putStrLn logs to the console.foreignimportjavascriptunsafe"console={ \ \ log: function(s) { java.lang.System.out.print(s); },\ \ info: function(s) { java.lang.System.out.print(s); },\ \ warn: function(s) { java.lang.System.out.print(s); },\ \ debug: function(s) { java.lang.System.out.print(s); },\ \ error: function(s) { java.lang.System.err.print(s); }\ \ }"setupConsole::IO()demo=runST$dojlist<-listToArrayList.maptoJSInt$[1..5]iteratorjlist>>=iteratorToListmain=dosetupConsolemapM_(putStrLn.show.fromJSInt)demosysexit0
In the code above, two Java types are used: java.util.ArrayList and java.util.Iterator.
Importing a Java class
A Java class can be imported with Java.type(className) Nashorn JavaScript code. Line 80 defines the corresponding Haskell FFI function:
123
-- Imports a Java classforeignimportjavascriptunsafe"Java.type($1)"jimport::JSVal->JType
Creating an instance of a Java class
An instance can be created by invoking the constructor on the Java class with new. Here is the corresponding FFI:
It takes the ArrayList class and invokes the dafault ArrayList constructor to return an instance of it. In the same way, we can create FFI functions for ArrayList.add and ArrayList.iterator to return an Java Iterator instance.
The function listToArrayList takes a Haskell list and return an instance of Java ArrayList. As the java list is mutable, it is returned as STArrayList s inside ST. This function first creates an instance of ArrayList by invoking the Java constructor and then calls ArrayList.add to add items from Haskell list to the ArrayList.
In the similar way, the function iteratorToList takes a Java iterator and returns Haskell list by extracting items from the iterator by invoking corresponding FFI functions for Iterator.hasNext and Iterator.next.
Building with Stack
It is easy to setup a GHCJS project with Stack so that we can add other dependencies easily and build it for GHCJS. With the above code in a stack project “haskell-jvm-hello”, we can build it with stack build and run it with jjs:
Java’s Nashorn JavaScript engine opens up few more ways for the JVM to be polyglot and it is so good to have one of the best languages, Haskell, on the JVM. Actually it should also be possible to run PureScript as well in this way on the JVM but that is for another day. Happy Haskelling!