Elm/TEA for projectional DSL editing in the web

Hello,

Disclaimer: I’m neither very familiar with Elm, nor am I a web developer, so I might say some stupid things about Elm and web development. My apologies.

Context
In the “Model-Driven Development” community, there is an ongoing discussion how we could best bring “projectional editing for domain-specific languages” to the web. What I mean by that is that there are people (me included) that use technologies like JetBrains MPS to build domain-specific languages for non-programmers, but MPS only allows us to build desktop applications, which entails a lot of problems, as you might be able to imagine. While there are a bunch of approaches trying to bring projectional editing to the web, nothing has stuck yet (to my knowledge).

What do you mean by “projectional editing”?
In the context of language engineering it means that, instead of having a textual representation of a program in combination with a parser to produce an AST, you have a “projection” of an AST that lives in memory and you interact directly with that projection. This figure by Markus Völter I find to be very compelling (source: slide 14):

projectionalEditing

Why Elm?
I believe that Elm and in particular TEA are a very natural fit to implement “projectional editors” for the web. One could argue that, by definition, an app written in Elm is a projection: The view always shows the model (read: AST) in memory as defined by the “view” transformation (read: concrete syntax).
So, my post could end right here with the following, unsurprising takeaway: Elm can be used to build projectional editors for the web, and with the right model, you might even call your app a DSL, yay!

Problems in practice
I’ve looked into how one would go about building a “editor” for a simple DSL in pure Elm. At that point, the focus is not on features like static analysis, scoping, or the like, it’s just about how to model a simple “DSL” in Elm and make it editable via Html. This makes the line between what a DSL and what an application is disappear, I’m aware of that. But that’s a good thing, because I think that this means all the problems I encounter are not specific to building DSL, but general to writing applications in Elm.

Updating nested records
This seems to be a heavily discussed topic, and I’ve read through various sources and threads, e.g.

A common theme for the domain of “web apps” is to try and keep your model flat and arguments against new Elm language features that would make it syntactically easier to access and update nested record fields have been labeled as “leading in the wrong direction”. I can understand that, given the context of web apps.

Unfortunately, when the app you are trying to build is a language, you inherently get deeply nested structures. That’s just (programming) languages often are.
So, how would you comment on the following claim:
Claim: If the domain you want to build apps for inherently features deeply nested structures, Elm just isn’t the right tool in it’s current state, and will likely never be.

Targeting an intermediate model instead of HTML
When building projectional editors for DSLs, you often want certain behaviors in the editor, independently from the actual language. Imagine a portion of Martin Fowler’s state machine DSL, where you want to have a web editor for defining events that looks like this:

events

  • navigation: ArrowKeys allow to navigate between the input fields for the items
  • insertion: “ENTER” adds an item (here, an event)
  • deletion: Backspace/Delete on an empty item deletes the item

There are way more “default behaviors” one might imagine to have, e.g. range selection, element movement, or drag and drop. It would be a pain if “language engineers” would have to write all this behavior every time they want to build a language with Elm.
Ideally, I would like to have a package that allows users to, instead of transforming their application model into Html (view function) and having to implement all this behavior from scratch every time they build a DSL, to transform it into an intermediate model (I call it a “Cell” module) which, in turn, manages all these interactions. For example, if I would want the behaviors as described above for the vertical collection of input fields, one could just call a function in the “Cell” module that produces a specific Cell variant that already knows how to handle the messages for navigation, insertion, and deletion.

I found different suggestions how the whole communication between modules should be done, including a translator pattern, a “taco pattern” to have some state shared between model (which is discouraged), and a bunch of other discussions about if and when to use Html.map.

At that point, I’m not sure what the “right” approach would be to structure an Elm application with a intermediate model between the application model and the Html.

I am looking at UI packages, like mcw-elm to learn how it is done there, but I would love to hear if people think this is the correct way to go?

Conceptually, I would have thought that TEA should be used like this, at least that’s my current approach:

Both the main model and the cell model are given by my “projection module”, the user (a language engineer) just models their DSL types (language structure) and defines a Msg type to react to domain-specific messages.
I guess my question here is if this seems like a viable approach, or if I’m about to run head-first against a wall that others, more experienced Elm users have been hit before?

If you have read until now: thank you, I appreciate your endurance.

6 Likes

Aside from sounding like a fairly complex project, I can’t see any reasons why it would not be possible.

One way to satisfy yourself of this might be to outline the systems types and major functions, in order to better understand how it all hangs together. I sometimes sketch out types and functions for major areas of code that I am unsure of, and as I do so, things begin to look clearer and I can see the way forward.

So define your Cell type, and CellMsg, and some view and update functions and so on. You don’t need to implement them, you can use Debug.todo, and this will match any type. Put the type signatures on any such sketched out functions too, and then compile it and see that the compiler is happy with it all.

type Cell = ... -- Sketch out your cell type.

-- Some update function you might want.
update : CellMsg -> Cell -> (Cell, Cmd CellMsg)
update _ _ = 
    Debug.todo "later dude..."

-- And so on...

Sounds interesting, I never really grokked JetBrains MPS, but I look forward to seeing what you come up with.

4 Likes

It made me think of this presentation, “Structured Editing for Elm* in Elm” by Ravi Chugh. Maybe the author will have interesting feedback for you.

The webpage for their work on this project: https://ravichugh.github.io/sketch-n-sketch/

3 Likes

Thank you very much for this pointer, this is super interesting to me, and indeed very much related to what I’m looking into.

2 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.