An Elm REPL for my phone

I was working on an online REPL similar to the one in the Elm guide or Online REPL and Mini-IDE but with a focus on using it on my mobile phone when I don’t have internet access.

The result (for now, more below) is elm.run/repl

Usage

There is an input area at the bottom where I can enter multiple lines of Elm code. The input field grows as needed, and I tried my best to ensure that it is usually not hidden by the on-screen keyboard on my phone.

With every keystroke, the code is checked and if it is valid I will see the type definition above my input (in the example the declaration elm = "delightful") and the evaluated result below, which is the String “delightful”.

If I press the “run code” button, it will print the entire compiler output, which is useful for finding errors like in the example where a number was supplied insted of the expected String to create a Tree type alias.
The formatting is supplied by the compiler and looks like it would in the console.

I can scroll up to see older code that I entered, can easily copy each down into the input area with one button press or remove it completely.
It also has a button to execute multiple bulk clean-up operations. From a full reset to removing all outdated declarations to only removing errors, I have multiple options there.

For people just starting out, I added an introductory text at the top of the page and a button to insert example code.
Right now it randomly picks one of several examples each with multiple entered expressions.

Result for now?

The REPL UI is built in Elm, and uses ports to communicate with the Elm compiler compiled to WebAssembly. The glue code is written in TypeScript. Both are available on github and I wrote a little about it in this post.

The code is far from ready, but after the Elm-in-Elm compiler rewrites were released in the past weeks, I will check if I can use them instead.

  1. GitHub - pithub/elm-compiler-in-elm-ui: UI for the Elm Compiler in Elm
  2. GitHub - guida-lang/compiler

Because I’m working slowly, there won’t be any visible changes in the next weeks (I guess), so I decided on posting it already.

24 Likes

Wow! This is truly impressive! :clap: :clap: :clap:

Is this the first instance of the Elm compiler being compiled to WebAssembly?

I’m really curious about how it stacks up against the elm-in-elm compilers in terms of performance and size.

1 Like

This is super cool. :sunglasses:. Definitely going to follow this. :slight_smile:

Thanks :blush:, I don’t know who else also used the GHC WASM backend to compile the Elm compiler.
But back in June, at least Evan had not yet done so (or planned to). He had some ideas though to reduce the binary size by only compiling one file at a time (which I have not tried yet).

If one starts with the full elm compiler and then only removes what does not compile on wasm32-wasi (like http calls and some file IO), the effort is low and not many changes are needed.

Starting with my own fork, and then with elm-dev and then with Evan’s compiler, always trying to go from the minimal state and then adding more functionality as I got more working was very slow and not worth the effort :sweat_smile:

I’m also interested in comparisons, but I think for my use cases of REPL and a few hundred lines of Elm code the Elm-in-Elm implementations will be the better fit.

Guesses for comparisons

My first guess is that the wasm compiler will not be smaller than 1M in transferred size and that the Elm implementations will be.

I don’t have much experience with WebAssembly, but for me wasm files were always surprisingly big and getting e.g. rust or c below 0.5M required many tricks. A hello-world from ghc-wasm weighs ~1.2M uncompressed.

About speed:
Evan mentioned that the algorithm for inferring types needs mutable state, so that might slow down the Elm implementations.
But for smallish files, I expect that the overhead for all the string<->binary conversions and executing WASM will favor the Elm implementations. Even if the wasm compiler should be faster on bigger files - or if type annotations are missing.

3 Likes

Hi Marc, this is really fantastic! I’m on vacation right now so I currently have even less time than before, but I’d love to do some comparisons.

It’s funny that you wrote:

The code is far from ready, but after the Elm-in-Elm compiler rewrites were released in the past weeks, I will check if I can use them instead.

because I was just thinking that I should check whether I can use your code instead of mine for my purposes :sweat_smile:, so please don’t abandon your project!

1 Like

It might be the first instance of the Elm compiler compiled to WebAssembly, but it’s not the first to run the (actual) Elm compiler in the browser. Ellie used to (but doesn’t anymore) compile the compiler to JS using GHCJS: https://youtu.be/gNWx-zWxUd4?si=UmBE8QAcWDtDIrdR&t=361

(It was removed in 0.19 by lukewestby · Pull Request #42 · ellie-app/ellie · GitHub)

1 Like

Thank you for sharing this link to “Putting the Elm Platform in the Browser”, they definitely did a better job than me, I think my GHCJS builds were around 20M instead of their initial 13. :smiley:

Does someone happen to know why it was removed?
Or was it more more that it was just never added again after the backend rewrite @luke?

@pit

because I was just thinking that I should check whether I can use your code instead of mine for my purposes :sweat_smile:, so please don’t abandon your project!

I’ll trade you for a bottle of nice local beer (except for Zäpfle).

Now, seriously: I don’t intend to stop or remove the code, but I think for the REPL the wasm build is too big. Even if it might be used similarily to the official guide inside a text-based tour.
But I intend to explore other ways of writing/running Elm code in the browser and want to check out options.

And I’m also interested in translating the error messages and loading several translations into Elm programs would be easier than building multiple wasm binaries.

1 Like

I don’t have a source, but as far as I remember one of the reasons was that Elm 0.19 started using some low level Haskell thing for performance that wasn’t supported by GHCJS.

1 Like

I started with a few tests over the past week and don’t want this thread to close while I’m away on the weekend, so I will now post already a naive size comparison

Naive size comparison overview

As an overview, I got these download sizes (gzipped) of the compilers:

But this comparison is unfair, because the Elm implementations

  1. were not compiled with the --optimize flag. They both contain Debug statements, so they will be smaller and faster without those.
  2. contain more functionality: init, install, repl. The wasm compiler only runs different variants of make.

Longer

> git submodule  
51cdc1a4e4488828d531cbdcf6b2e935d70b8826 compiler/guida (heads/master)  
4ea648a53149451b4942a84ac72ebfde24b718e9 compiler/marc136 (heads/main-wasm)
7823c804c3895a1ab962c8a3a347b9752467aa18 compiler/pithub (heads/main)

# 1st compiler
[4.0K]  compiler/guida/bin/
├── [3.3M]  guida.debug.js
├── [412K]  guida.debug.js.gz
├── [3.3M]  guida.js
├── [412K]  guida.js.gz
├── [843K]  guida.swc.js
├── [206K]  guida.swc.js.gz
└── [ 11K]  index.js

# 2nd compiler
[4.0K]  compiler/pithub/dist/
├── [ 216]  eval-elm.js
├── [4.2K]  favicon.ico
├── [3.1M]  index.debug.js
├── [445K]  index.debug.js.gz
├── [ 289]  index.html
├── [2.9M]  index.js
├── [413K]  index.js.gz
├── [744K]  index.swc.js
├── [204K]  index.swc.js.gz
├── [ 27K]  source-code-pro.ttf
├── [ 34K]  source-sans-pro.ttf
└── [3.1K]  styles.css

# 3rd compiler
[4.0K]  compiler/marc136/dist
├── [4.6K]  repl.js  
├── [4.8M]  repl.opt.wasm  
├── [1.3M]  repl.opt.wasm.gz  
├── [6.3M]  repl.wasm  
├── [1.4M]  repl.wasm.gz  
├── [4.6K]  ulm.js  
├── [5.2M]  ulm.opt.wasm  
├── [1.4M]  ulm.opt.wasm.gz  
├── [ 10M]  ulm.wasm  
└── [1.7M]  ulm.wasm.gz

To create the `.opt.wasm` builds, I use `wasm-opt`, but for some commits of ghc-wasm-meta it fails to parse the initial wasm output.
The 1.4M of ulm.opt.tar.gz is what would be downloaded in the browser, and it would 
execute the 5.2M.
5 Likes

Cool! I would recommend to make it PWA so can “install” it on you phone. Browser under the hood but feels like an app

1 Like

Thank you for reminding me about that @koorva

I added a simple manifest and service worker, so now https://elm.run/repl/ can be installed as a PWA and should work offline.

2 Likes

Thanks to @lue-bird’s new elm-syntax-format 1.0.0 package, I now added formatting of the Elm source code.

| |

I will need to make more changes to my code, because it does not yet format expressions like

if True then 1 else 2
4 Likes

Hey @marcw – think I’ve said it before, but this is super cool :sunglasses: thanks for the time you’ve spent building this!

Some minor UX feedback; even knowing Elm well I was quite confused for a bit with this part of the UI:

It wasn’t clear to me at first:

  • That the type signature was inferred and not editable
    • Maybe some slightly different styling treatment like a bg color and maybe a tiny label could help disambiguate it a little?
  • That the result at the bottom was auto-generated.
    • Given the “run code” button immediately to the left seemed to imply to me I would have to manually run it. I’m still left a little unsure what the run code is for! It seems more of a “save this to my history” ?
    • Also a small Result: label might be nice along with some extra spacing so it’s clear it’s separate from the code (like how I’ve manually added spacing in the screenshot).

Thank you for your feedback, a result label and more spacing would work well, I think.

Maybe it also makes sense to either show the evaluated result (if it returns a single value and not a function) or the inferred type.
Will need to dig into that, because the result is for now just js.