As the title asks: could merlin be compiled with js_of_ocaml and work in the browser? Has anyone attempted it?
Background: we’d like to make a real web only editor for simple ocaml projects. The features we are looking to provide with merlin is acurate type aware completion and display of module signatures.
Not only is this extremely cool all by itself, it looks to be a perfect demonstration/example of extending codemirror to support a custom language, with a lot of the bells and whistles one would expect, and all via OCaml and jsoo. Outstanding!
Thank you, glad you like it! We’ve ended up where we are thanks to various bits of hacking from @tmattio, @jonludlam, @voodoos (and I’m sure others!) and of course the jsoo contributors. This is how the OCaml.org playground works (although I think it lost merlin.js in the switch to OCaml 5).
Another nice feature (thanks to @jonludlam!) that might not be immediately apparent is that all of the OCaml evaluation is happening in a browser worker meaning it never blocks the UI (the adventurous can try while true do () done ^^), which has nice story if we compiled notebook style exercises to the browser or something like that.
Hmm not quite sure what you mean, but it sounds like just changing what triggers the evaluation (currently hitting the run button). So it definitely sounds doable :))
I’m an idiot. I was so blown away by the “Merlin the web” demo, I did not even see the “Try EIO” demo. Something else I don’t see – a LICENSE file, what license is “Try EIO” released under ?
Not naive at all, and yes (sort of)! You will need to build the JS files again, but it amounts to only changing dune files and some cmis – in particular:
Then probably do something with the top-level and the ppx that is being used, I don’t know if the browser top-level supports that, perhaps not.
I seem to remember a plan when we were hacking on this to one day have one of these top-levels for every OCaml Packages · Browse Community Packages (or at least those that compile to jsoo).
Yes, not being able to interact with the DOM is definitely a downside to this approach. There are some other upsides though, like dynamic loading of cmis/cmas - you can do synchronous fetches of remote files that you can’t do if the toplevel is running in the main page. This can cut the file size down by a surprising amount. I’ve got some patches almost ready that’ll do this for the playground on ocaml.org.
I did wonder whether it might be possible to have the compiler in the worker thread, and then inject the resulting javascript into the main page; this would allow for both the size fix and DOM manipulation.
I’m curious how the current Try Eio evaluation works. Is it:
(1) we have an OCaml interpreter compiled to js, and it interprets the *.ml in the codemirror editor or
(2) the OCaml interpreter compiles the *.ml in the codemirror editor to js, then evaluates it ?
From @jonludlam 's comment above, it seems to imply (2), i.e. the ability to take the js, postmessage it to another iframe, and evaluate there. However, my current (possibly incorrect) mental model is (1), where since it’s an interpreter, there isn’t generated js code to ship – unless we move the interpreter itself.
Sorry, I wasn’t precise enough. The way the toplevel works is that there’s this magic external function reify_bytecode that takes bytecode and returns a function that can be evaluated. The way the jsoo toplevel works is that it replaces that magic function with a javascript function that calls into js_of_ocaml to turn the bytecode into javascript, then injects it into the page (somehow?) and again returns a function that can be evaluated. We’d ship the bytecode back from the compiler worker thread then call that function in the main page.
I wonder if there is an example showing the complier running in the browser. What I mean is that an example where multiple .ml files (thus modules) in the virtual file system are compiled (by js_of_ocaml compiled OCaml compiler) and
the resulting JavaScript (assuming we define an entry file, link everything together, and use js_of_ocaml to transpile the bytecode to JavaScript) is loaded backed to the webpage and executed
allow merlin to type check module A that depends on module B while using signature information contained in the compiled b.cmi file.
Not exactly what you are asking for, but learn-ocaml does that kind of stuff, taking user-supplied ml code, applying some custom validations to its AST, then compiling and running it against a pre-defined solution. This and its sibling try-ocaml have had evaluation in a worker for a long time too — you can’t really ask students to reload the page after an accidental infinite recursion.
@patricoferris : I hope this does not come off as entitled / asking someone to do work for free. With the Try EIO example you have shared, can you provide a minimal example of *.ml code for doing self.postMessage ?
I know that the “OCaml interpreter” is being evaluated in a webworker.
I know that in regular JS, a webworker can send a message to parent via self.postMessage
I’m not familiar with the OCaml FFI; especially not in the case of jsoo.
I’m wondering if you could show minimal ocaml code (running in the jsoo / repl) which does the equiv of self.postMessage(…) [from webworker to parent]
===
The XY problem here is I’m planning on having the parent register a onmessage handler, then have the webworker (running jsso / ocaml repl) send it msgs via postMessage.