Open pchiusano opened 3 years ago
@pchiusano I think idea 1 is the most promising, and I have something like it working (albeit with my own version of MCode) in the unison.rs codebase. My rust/wasm runtime foundered when I tried to deal with garbage collection, because there just aren't the necessary primitives to do proper cross-boundary garbage tracking, such that holding on to handles to javascript objects results in a lot of garbage. with idea 1, we just rely on javascript's garbage collection to do everything, and it's a lot simpler. Idea 4 seems likely to be faster than idea 1, and it would definitely require having full type annotations on the AST, but I don't think it would be insurmountably hard. I do agree it's probably complicated enough to be a later-on project.
Exciting! This is going to be really interesting in exploring Unison tooling like documentation/code browsing, quick sharing of code in a jsfiddle way, rich editing experiences etc.
Just wanted to chime in and say I love this idea and hope it goes the distance! Thank you @pchiusano for this writeup, and thank you @jaredly for your work over in https://github.com/jaredly/unison.rs. I've been considering this idea and playing around with that codebase for a couple weeks now and wanted to contribute a few thoughts about what the future could hold on top of this:
Browser
ability (and likely some accompanying tooling, see 4 below) would pave the way for the rest of development toward this being library development inside Unison (right?), which is really great.elm-pages
-like interface, perhaps unison.cloud could also offer a Vercel-like service for building, deploying, monitoring and managing Unison web projects. view
and update
functions of the Elm Architecture, but also the traditionally static file assets - images like favicons, web app manifests, robots.txt, anything. I'll mention that elm-pages
looks like it takes a step in this direction, which I love to see. This endeavor might be restated as: "The Unison port of Elm's Program
type should be broadened to encapsulate any behavior or consideration a web site may need. Program -> <tooling> -> <directory(s) of html / js / css / png / * assets>
should be able to produce any web site achievable by other means"How absolutely neato would it be if the tooling that interpreted the Program
type acted similarly to (and therefore brought a similar level of delight as) ucm
? This is to say: something the user has a conversation with, that can watch what you're doing and accept direct input simultaneously. Perhaps something like:
Welcome to the Unison Web Project Manager!
list : print the list of Program terms in your Unison codebase
serve : serve a Program term to localhost, watch for updates to recompile and refresh
build : build static assets for a Program terms
list
I found 3 (2 unique) Program terms:
serve 1
Now serving Program under name .mysite.main (#a1bd51c...) at localhost:3000
Detected change to .mysite.main (previously #a1bd51c..., now #c1a2b53...). Updated assets at localhost:3000
This strikes me as an improvement to the standard you find around where the command line is the interface to various custom scripts, some of which may spawn a watch task that you can't meaningfully interact with further. Adding on to this lofty idea, perhaps this "Unison Web Project Manager" could be embedded into `ucm`, under some `ucm` plugin protocol.
Just to add to @samgqroberts first point: possibly having Elm-Architecture for Unison Frontend-Development would be absolutely (and beyond) excellent ! Also... given this georgeous Codebase explorer written in Elm, how could one not want that frontend quality for Unison itself ?
Given the amount of effort Unison demands generally (for reaching Release-Status), maybe there could be some intermediate frontend-solution that does not get in the way for some future Unison-Elm-Architecture (?)
It would be pretty nice to be able to use Unison for front-end development, especially if backend services are also written in Unison. Communication is seamless - just relocate a computation to the backend and have all the networking and serialization be done by Unison! No more sending JSON blobs around and issuing HTTP requests manually. No need to arbitrarily fragment your codebase into front-end and back-end code - you can share code between the two no problem, and you don't need some elaborate build setup to make it happen either.
The basic idea is introduce a new builtin ability
Browser
. It will be opaque, much like the existingIO
type, and we can fill it in over time with low-level browser functions in the Browser Object Model, for instance:Nothing creative here, just low level imperative functions for accessing the browser - we can add to it over time. It's expected that people will write higher level libraries on top of this.
We use
Browser
rather thanIO
because the environments are different! The UCMrun
command doesn't have access to theBrowser
ability! So within UCM you can write programs that useBrowser
, but you can't execute them.So how do you compile programs to JS for execution? The simplest thing is maybe a builtin function:
This would produce a single-page app. You'd take the result of that and save it to a file
myprogram.js
. I could imagine a few other compilation modes, like if you want to produce a JS library rather than an SPA.You probably want some FFI so people can invoke JS functions. I think the general approach outlined in #1404 makes sense: foreign functions are represented as operations of an ability. And then there has to be some way of telling the compilation function how those foreign operations should be translated to JS code.
Speculating here -
Open question - what is
FFI g
? It has to provide some way of converting all the operations ofg
into JS.Implementation ideas
Idea 1: MCode interpreter in JS
Putting aside the FFI for a minute, I wonder if we could write a simple
MCode
interpreter in Javascript (or any language that compiles to JS). We don't need to be able to interpret all theIO
primitives, just the pure builtins and theBrowser
builtins.Then the
compileToJavascript
is just producing a self-contained JS file that has some representation of the MCode needed for the program, and then the little embedded interpreter forMCode
. Calls toBrowser
builtins turn into regular JS calls. This won't be super efficient but is probably fine for a lot of front-end code.For FFI, it seems like you'd use the
FFI
argument tocompileToJavascript
to produce bindings for the builtin functions. MCode would have to support foreign calls somehow?If
MCode
is too late in the process, it may make sense to hook in at an earlier stage of the compiler, so perhaps thecompileToJavascript
is more like a UCM command rather than a function you can call at runtime (but it would be cool if you could call it at runtime).Idea 2: variant of @jaredly Rust runtime
This is really Idea 1, but using a Rust runtime rather than writing the interpreter directly in JS.
I think I'd want to start with a Rust runtime that basically is an
MCode
interpreter. So it shouldn't be responsible for things like reading definitions from a codebase or doing arbitraryIO
, just interpreting the IR for pure Unison code that can have embeddedBrowser
ability requests.@jaredly this is a little different than what you've implemented - it would be a lot easier since it's responsible for less. And we could recycle a lot of your existing work if we went this route. WDYT?
Idea 3: compile the entire Haskell runtime to JS
This feels kinda icky and I kinda doubt it will work very well. The Haskell runtime uses Haskell's
IO
type heavily and it just generally has access to a bunch of stuff (like file I/O, networking) that the browser shouldn't have access to.Idea 4: a proper compiler
This would try to produce actual JS code rather than something that is interpreted in JS. A difficulty here is we can't just reuse the JS call stack due to the stuff we need to do to support abilities (and also, like proper tail calls), so there'd have to be something fancy done here. Level of effort is probably very high.
/cc @dolio