Back in December 2017, I started working on something I called Elmish-Wasm, which was just an experimental repo to compile something Elm-like to Wasm. I made some progress, but the helpful people I talked and I came to the conclusion that most of what I had so far (Haskell and Regex to interpret .elm files into rudimentary wasm) was something the Elm compiler and the Elm AST already did. Writing that code was a lot of fun, but maybe not the most valuable way to explore Elm and WebAssembly. Before jumping back into this project I would like to record the important facts and questions related to compiling Elm to Wasm.
A lot of people talk about Web Assembly as if its C++ that runs in the browser. Thats not the case. This belief must come from that fact that C can currently compile to web assembly. Wasm is human-unreadable bytecode. There is a human-readable version of wasm, called wat. It looks like this..
;; A function that adds two numbers
(module
(func $addTwo (param i32 i32) (result i32)
get_local 0
get_local 1
i32.add
)
(export "addTwo" (func $addTwo))
)
This code exports a function called addTwo
into JavaScript. It adds two numbers together..
Every animation frame the Elm virtual dom builds a virtual representation of the html page, finds the differences with a virtual representation of the prior html page, and then implements those differences in the dom. If that entire process could happen in Wasm, the virtual dom would become much faster. We would like to minimize the fixed performance cost of crossing into Wasm from JavaScript, which ideally would only happen twice per animation frame: once to show Wasm the new model, and once for Wasm to show JavaScript the changes that need to be made to the existing Html.
A step farther would be to keep the Elm application's model in Wasm entirely, so it doesnt have to cross into Wasm from JavaScript at all.
Wasm isnt really like C syntax, and it seems to resemble ML syntax in the way that functions are naturally applied to whatever preceeding byte values happen to be there. Heres what I mean
i32.const 3
i32.const 4
i32.add
This code initializes a value of 3, then initializes a value of 4, and then adds them together. The add function has no "awareness" of what the two preceeding values are, it just adds whatever they may be together. Web assembly functions cannot return functions, so pretty plainly precludes currying.
But there must be some way to curry. One hint might be to not compile Elm functions to Wasm functions directly. For example, consider this snippet..
i32.const 1
i32.add
This snippet is basically waiting for a value to be put above the top line so it can add one to it. Its basically a function, and if it could just be inserted or applied to other snippets of wasm it would be. Dynamically generating wasm snippets sounds impossible or greatly inefficient. But I am probably missing something, since programming languages that do curry do compile to assembly. There must be a way.
Of course we dont have to compile Elm to Wasm. We could compile it to C and then Wasm. But I dont know that C is any more capable of currying than Wasm. Googling "currying in C" shows real results, but I dont take that to mean its fully possible in the narrow context of web assembly.
Wasm is really fast. Most people understandably dont feel like performance is a priority; but others are trying to get websites to render in as few milliseconds as possible after page load, and others still are making software that actually does struggle to run given the performance constraints of the browser. Performance is good, some times necessary, but at least cant hurt.
But its not the raw performance thats really interesting. Whats a lot more interesting is the potential features high performance code could unlock. Today if you wanted a super high resolution computer generated video or a complicated 3D rendering, its probably out of reach of your users CPU. If the performance is there then its not out of reach. Today you can write JavaScript that compiles to a mobile app, but its an inferior option because the performance isnt there. More performance means JavaScript is a more valuable and more universal development tool on more platforms.
My personal hope, is that WebAssembly can bring all of Web Development back down to the very basics of computing, whereupon web development can build itself back up, free of the old ways of html and css. The performance doesnt matter, so much as the performance makes it viable to build new web fundamentals from scratch. WebAssembly is a way to escape the legacy of the internet.
Whenever you bridge between two programming languages, theres always a huge performance penalty per crossing. Minimizing this penalty means maximizing the amount of run time that happens on the wasm side of JavaScript to Wasm connection. Excluding this fixed performance penalty, the best estimate I have come to, is that Wasm code takes 3% as long to run as JavaScript code.
How about analysing how curried
swift
is translated towat
Also
swift
hasenums with parameters
that seem very similar toElm
'sunion types
.Would also be interesting to see this as
wat
.For now the idea to generally transpile Elm to swift instead of JS is not yet a valid option, as the swift compiler is only supported on Ubuntu and macOS. But I like to dream on.