Elm compiler written in Elm
Elm compiler written in Elm!
elm-in-elm
is focused on readability, beauty, approachability, simplicity, great docs and great tests first, and only then completeness and speed.where
syntax, etc.In short, elm-in-elm
aims to unblock and encourage people to play with compilers and the Elm language itself, explore new frontiers and have fun!
:tv: For more context and information, you can watch Martin Janiczek's talk from Elm Europe 2019 which served as an unveiling of elm-in-elm
to public. Here are :bar_chart: the slides.
This is :negative_squared_cross_mark::negative_squared_cross_mark::negative_squared_cross_mark: NOT THE REASON and NOT THE GOAL :negative_squared_cross_mark::negative_squared_cross_mark::negative_squared_cross_mark: of elm-in-elm
. We don't want to and aren't planning to divide the community into multiple Elm derivatives, and will actively try to prevent that. elm-in-elm
is, for all intents and purposes, a sandbox, a place to try out ideas, an experimentation environment.
elm-in-elm
consists of:
It is written in Elm, and compiles Elm to JavaScript, but lays the foundation to be able to compile to different targets in the future.
:warning: Warning!
elm-in-elm
is definitely not ready for usage yet, even though its library is published already. The main blocker is the parsers for expressions not being all implemented yet. See theparse
column in the table below.
Please yes! :heart: Feel free to look around the help wanted or good first issue issues, have a look around the codebase for some general nitpicks or refactorings, or hit us up on Discord!
parser tests | optimize tests | emit tests | parse | desugar | infer types | optimize | emit | |
---|---|---|---|---|---|---|---|---|
integers | :heavy_check_mark: | :warning: [2] | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :warning: [2] | :heavy_check_mark: |
floats | :heavy_check_mark: | :x: [5] | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: [5] | :heavy_check_mark: |
characters | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
strings | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
booleans | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
variables | :warning: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
lists | :heavy_check_mark: | :x: [6] | :warning: [1] | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: [6] | :warning: [1] |
binary operators | :warning: [3] | :heavy_check_mark: | :heavy_check_mark: | :warning: [3] | :heavy_check_mark: | :x: | :warning: | :warning: |
lambdas | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :warning: | :heavy_check_mark: |
function calls | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
if...then...else | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
let..in | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :warning: | :heavy_check_mark: | :warning: |
case...of | :warning: [4] | :x: | :x: | :warning: [4] | :x: | :x: | :x: | :x: |
records | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
record accessors | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: |
record updates | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: |
unit type | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
tuples, 3-tuples | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
type annotations | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
type aliases | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :warning: [7] | :heavy_check_mark: | :heavy_check_mark: |
custom types | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :x: | :x: | :x: | :x: |
custom operators | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: |
shaders (?) | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: |
emit
will have to change a bit: conversion from target platform lists to Elm "custom type" lists is now missing; tracked in #29
Q: Why not use stil4m/elm-syntax
for the parsers?
A: We'd love to use elm-syntax
- it would save us so much trouble. But that would not be ideal in some regards:
elm-community/parser-combinators
style - we'd like, again because of the learning resource goal, to have the parsers written in idiomatic elm/parser
styleBut yeah, there's definitely a little bit of NIH syndrome happening :wink:
The easy way: if you have Nix installed, run
$ nix-shell
and you'll drop into a shell that has all the dev dependencies set up and ready!
Alternatively, this is what the project needs.
$ make
Essentially compiles the compiler (using the official Elm compiler :wink: ) to a build/elm.js
file and runs it using node
.
Very handy for running the whole compiler pipeline on an example project living in example-project/
, which the CLI is currently hardcoded to try and compile! In some cases this might be more convenient than writing tests - just add an interesting snippet to example-project/src/Main.elm
, Debug.log
what you need in the compiler itself, and make
!
So absolutely feel free to go bonkers on that example-project/
- it's there for developer convenience!
$ make test
Runs elm-test
on the test suite (gasp!)
$ make format
Runs elm-format. Make sure to format code before submitting a pull request!
This is a brain-dump of some low-level stuff. (High-level stuff should be in the roadmap.) My apologies if it's hard to make sense of this! ~janiczek
elm-in-elm
(ie. talks about Haskell hierarchical optimizations etc.)Main.compile
with official compiler's Compile.compile
- is that a better API?Stage.InferTypes.generateEquations
, the Typed.Let
case. This paper might have a parable written well enough that we might actually understand type schemes from this. Otherwise, "Write you a Haskell" for the rescue! There is also the Damas and Milner paper proving the inferred type is the most general one.D. Leijen, “Extensible records with scoped labels,” in Revised Selected Papersfrom the Sixth Symposium on Trends in Functional Programming, TFP 2005,Tallinn, Estonia, 23-24 September 2005.(M. C. J. D. van Eekelen, ed.), vol. 6 of Trends in Functional Programming, pp. 179–194, Intellect, 2005.
a
instead of type #0
or something). Stage.InferTypes.getType
(<|)
and (|>)
fusion (eg. transform both x |> f
and f <| x
into f x
)Lambda
case of Stage.PrepareForBackend.findDependencies
works correctly(a,b) => a+b
instead of (a) => (b) => a+b
)?let
? How does official compiler do it? Seems the dependency graph will have to be computed for its binidng too, similarly to how the path to main
gets computed for the program itself. Stage.Emit.emitExpr
, the Let
case.Common.unalias
Stage.Desugar.findModuleOfVar
Martin Janiczek | Rémi Lefèvre | Harry Sarson | Maxime Dantec | Aaron Janse |
Eduard Kyvenko | Sébastien Besnier | sgdan | Pablo Hirafuji | You? |