Hello there,
Jordan recommended I post this here rather than on the purescript Discord, so here it is!
I’d like the thoughts of the community on some ideas. I’ve been playing for a couple of weeks with using purescript in ObservableHQ (using Try Purescript / Jun Matsushita | Observable) and it’s been really enjoyable (see also my discord announcement). I used to stream for a bit, but that came with quite a lot of overhead, whereas working in a public computational notebook has the same feel of “working in the open” with a much lower barrier to entry. I think also it’s a different paradigm than an IDE and I’m quite interested in thinking about what it would take to take this further than a “scratchpad” for small ideas.
Today
Right now I can interleave my notes with code snippets and work in an editor cell and ask the try.purescript.org compiler to check my work and run the result in the notebook. The purescript JS interop made it quite easy to get there and I feel like I’m only scratching the surface of what’s possible.
As you can see in the try-purescript
notebook and the screenshot attached, it’s pretty much an alternative frontend to try purescript with missing features but also some new possibilities. In fact it’s almost easier that with try.purecript.org as you don’t need the TryPureScript
module’s helpers to display html, and there’s no complications with the iframe. You can also interop with any JS in your notebook, but also imported modules and other notebooks. Much wow.
Tomorrow
There are a few things that could improve the experience that are more specific to observableHQ:
- I’m using codemirror with the haskell syntax highlighting, having purescript specific syntax highligting would be nice.
- There are some bugs with the codemirror integration (tab to indent doesn’t work and focus switching has quirks) and purescript cells don’t feel first class (Cmd-Enter to save and compile would be nice), I reused a localstorage module to persist code in the browser but that’s not as reliable as I’d like.
- I think it should be possible to save the compiled JS returned by the try purescript API as a file and attach it to the notebook and load it, such that you can reuse observable purescript notebook as javascript modules.
A bit more involved, but I’ve tried this with relative success it’s already possible to run the trypurescript compiler locally and point your notebook to 127.0.0.1 to extend the package set. So with this approach it would also be possible to improve the experience by:
- running a language server locally and use codemirror’s support for LSP to have all the nice things.
- maybe making it easy to push button deploy your compiler and language server to some cloud service with your customisations?
At this point though, you’re running all of this extra stuff locally and it feels a lot like a full local development environment, except you’re using a somewhat less capable frontend (ObservableHQ) instead of your own IDE.
Using the public try.purescript.org compiler API, I think is ok (I hope ) if done in the spirit of try.purescript.org
itself. And should allow more people to try purescript, but I guess that there’s the potential that the compute bill goes up. And beside the cost aspect, this isn’t built to deal with this use case and scale. But what about also deploying a language server with try.purescript.org
? It could benefit the main website too if the editor used codemirror and it’s LSP facilities.
Would there be appetite for contributions in this direction? Or would it be best to package this independently for local or other private hosting options?
Sometime Maybe?
Finally I wanted to address some more fundamental limitations which would require more work to be done, and hurt my head when I think about them and where I’d like most to hear feedback about.
I think my main question, after using this approach for a bit, and what’s missing the most is “What is needed so that I could import a purescript module written in one cell/notebook, into purescript code written in another cell/notebook?”
With bonus points to making this reactive (which is really the observableHQ innovation – props to jupyter where this was incubated – together with reusing cells/notebooks Imports | Observable documentation ). But I think this is fairly orthogonal, and also I don’t know yet how welcoming the ObservableHQ folks would be in general to compile to javascript use cases. So let’s leave this aside.
Not our problem
I suppose the path of least resistance is what I was hinting above, which is to consider that this is not a purescript question, and we treat compiled purescript as ESM modules and bye bye types, we interop with purescript code with Foreign. Each cell compiles it’s own Main module, and I try and make this fit nicely with the reactive imports in Observable. In this case we’d still be missing FFI : You can import PS modules in the registry that have foreign code, but right now you can’t ask the try purescript compiler endpoint to deal with your Main.js (Add foreign module and HTML editor · Issue #8 · purescript/trypurescript · GitHub) and we don’t have the foreign import """console.log('yay')""" blah
loophole anymore.
But that introduces of course quite a lot of friction, when why I’d like really is to declare a module in a purescript cell and import it in another.
So the 3 other options I can think of right now are:
Compile external code
Could we allow the compiler endpoint (maybe as a flag) to link to code that’s not in the registry, or we extend the concept of registry to enable custom registries and allow to specify that some module’s purescript code is hosted in an Observable notebook’s cell (or as an attachment). Maybe we also allow to compile modules other than Main
and compiler API calls allow to say “compile this, together with these other modules as dependencies which are in these cells”.
Link to external modules
Is there a middle ground where already compiled Purescript code is hosted together with its externs.cbor
data and we don’t need to recompile modules which already have been compiled. So we’d pass forward to pass something like a module map of externals to the compiler endpoint saying, just map these modules to this module specifier and don’t try to compile it?
Purescript Compiler as a Wasm module
Another tack on this would be to build the purescript compiler (and language server?) as a wasm library. It seems that the GHC Wasm backend has gotten some momentum recently, so it might be feasible?
Thank you for reading up until here. As you can tell, I had a lot to unload, and I appreciate the effort
Looking forward to hear any and all of your thoughts!