Elara is a purely functional programming language targeting the JVM. It features a Haskell & F# inspired syntax, a complete Hindley-Milner type system and pure functions in the type system (i.e. an IO monad).
Please note that Elara is currently a work in progress and is not yet ready for production use.
def main : IO ()
let main = print "Hello, World!"def factorial : Int -> Int
let factorial n = if n == 0 then 1 else n * factorial (n - 1)def sum : [Int] -> Int
let sum ls =
match ls with
[] -> 0
x::xs -> x + sum xs
def main : IO ()
let main = print (sum [1, 2, 3])
-- Prints 6def map : (a -> b) -> [a] -> [b]
let map f ls =
match ls with
[] -> []
x::xs -> f x :: map f xs
def main : IO ()
let main = print (map (\x -> x * 2) [1, 2, 3])
-- Prints [2, 4, 6]type Option a = None | Some a
def map : (a -> b) -> Option a -> Option b
let map f opt =
match opt with
None -> None
Some x -> Some (f x)
def main : IO ()
let main = print (map (\x -> x * 2) (Some 3))
-- Prints Some 6If you're interested in Elara or contributing to its development, join our Discord server for frequent updates and discussions.
Elara is extremely buggy and temperamental at the moment, but it should function!
- To run Elara you need a JRE. Anything above Java 8 should work
- Before running, and if you change the files, make sure to rebuild the Java standard library:
cd jvm-stdlib
javac Elara/Error.java Elara/Func.java Elara/Func2.java Elara/IO.java Elara/Int.java Elara/Prelude.java Elara/Unit.java Elara/Func0.java
cd ../Currently the structure of an Elara program is very rigid: the source.elr file in the root directory must contain a Main module with a main function of type IO (), and all other code must be part of the standard library, in the stdlib directory.
Elara owes its existence to numerous open-source projects, particularly to Grace for its use as a reference implementation for type checking.
Elara is released under the MIT License.
The Elara compiler is fairly simple, composed of multiple passes on the source code. These are as follows:
- Lexing: The source code is converted into a list of tokens, layout rules are converted into braces and semicolons, and comments are removed.
- Parsing: The list of tokens is converted into the Frontend abstract syntax tree (AST). The Frontend AST almost directly mirrors the syntax of the language with very few transformations applied. The Parser checks for any syntax errors and reports them.
- Desugaring: The Frontend AST is converted into the Desugared AST. The Desugared AST is a bit misleading in that it is actually fairly conservative in its desugaring. The main differences are that multi-argument lambdas are converted into nested single-argument lambdas, let bindings have their parameters removed and are converted into lambdas, and declarations with identical names (i.e.
defandlets) are converted into a single declaration. - Renaming: Conversion to Renamed AST with name resolution and unique-ification. Every name is either resolved to a fully qualified name, or local variables are generated a unique name to avoid collisions.
- Shunting: Conversion to Shunted AST with operator precedence and associativity applied. A simple reassociation of the AST to match the precedence and associativity of operators. The notion of a "binary operator" is removed as they are all turned into prefix function calls.
- Type Checking: The Shunted AST is converted into the Typed AST by inferring and checking the types of every expression.
- ToCore: The Typed AST is converted into the Core AST. The Core AST is a very simple AST that is essentially a typed lambda calculus, very similar to GHC's Core language. Very extensive desugaring is done here, converting the entire language to the 8 constructors of the Core AST.
- CoreToCore: Repeated transformations of the Core AST, mainly performing optimisations.
- Emitting: The Core AST is converted into JVM bytecode and written to a class file.