JVM bytecode back end for Idris
- Idris with libffi. To install Idris with libffi using cabal:
cabal install idris -f ffi - Java 8
- Download and extract JVM bytecode back end from here. Make sure to download the release corresponding to your Idris version.
- Define
IDRIS_JVM_HOMEvariable with extracted directory path - From the extracted directory, run
$IDRIS_JVM_HOME/bin/installto install Idris packages for idris-jvm. - Add
$IDRIS_JVM_HOME/codegen/bintoPATH.
-
helloworld.idr
module Main import IdrisJvm.IO data Tree a = Leaf | Node (Tree a) a (Tree a) inorder : Tree a -> List a inorder Leaf = [] inorder (Node left a right) = inorder left ++ [a] ++ inorder right tree : Tree String tree = Node (Node (Node Leaf "3" Leaf) "+" (Node Leaf "7" Leaf)) "/" (Node Leaf "2" Leaf) main : JVM_IO () main = printLn $ inorder tree
-
Compiling
- On Linux/Mac OS:
$ idris --portable-codegen jvm -p idrisjvmffi helloworld.idr -o target - On Windows:
idris --portable-codegen jvm.bat -p idrisjvmffi helloworld.idr -o target
- On Linux/Mac OS:
-
Running
- On Linux/Mac OS:
$ java -cp $IDRIS_JVM_HOME/idris-jvm-runtime.jar:target-classes main.Main - On Windows:
$ java -cp $IDRIS_JVM_HOME/idris-jvm-runtime.jar;target-classes main.Main
- On Linux/Mac OS:
-
Idris basic types are mapped to Java types. Idris
Intis mapped to Java primitiveint. IdrisStringis mapped to JavaString. IdrisIntegeris represented as JavaBigInteger. IdrisDoubleis mapped to Javadouble. IdrisBits8,Bits16,Bits32are mapped to Javaint. IdrisBits64is mapped to Javalong. -
Tail recursion is eliminated using JVM's
GOTO. For the following code,sum 50000wouldn't blow up the stack.sum : Nat -> Nat sum n = go 0 n where go : Nat -> Nat -> Nat go acc Z = acc go acc n@(S k) = go (acc + n) k
-
Non-recursive tail call is handled using trampolines. For the following code,
evenT 10909000007would work just fine and return the result after few seconds.IOis used here as otherwise Idris inlines the function calls and the functions end up being tail recursive instead of mutually recursive. Non-recursive tail calls are delayed using Java 8 lambdas with JVM'sinvokedynamic.mutual evenT : Nat -> JVM_IO () evenT Z = printLn True evenT (S k) = do setProperty "bar" (show k) oddT k oddT : Nat -> JVM_IO () oddT Z = printLn False oddT (S k) = do setProperty "foo" (show k) evenT k
-
FFI - Calling Java from Idris: From Idris, invoking Java static methods, instance methods, constructors are all supported. See here for an example.
-
FFI: Calling Idris from Java: Idris functions can be exported as Java instance methods, static methods and constructors. The exported class with Idris implementations can extend a Java class and implement interfaces. It can have static and instance fields and the field values can be set from Idris. Idris types (monomorphic, for example,
List Int) can also be exported as a Java class. See here for an example. -
Idris functions as Java lambdas: Idris functions can be passed as Java lambdas in FFI. JVM's
invokedynamicinstruction is used to create target functional interface objects just like how javac does.
main : JVM_IO ()
main = ArrayList.fromList ["foobar", "hello", "world"] >>=
stream >>= -- Java 8 Stream from Java's ArrayList
filter (jlambda (not . isPrefixOf "foo")) >>= -- Idris function as Java "Predicate" lambda
map (jlambda Strings.reverse) >>= -- Idris function as Java "Function" lambda
collect !toList >>=
Objects.toString >>=
printLn-
Maybetype can be used in an FFI function to avoid Javanullgetting into Idris code.Maybeused in an argument position will passnullto the Java code if the value isNothingotherwise the unwrapped value will be passed to Java. In the similar way,Maybetype used in the return type position would returnNothingif the FFI function returnsnullotherwise returns the actual value inJust. -
Idris functions can be exported with Java annotations. This enables Idris to be used in Java's annotation based libraries such as Java Microbenchmark Harness, Spring Boot etc. See here for an example.