Skip to content

Instantly share code, notes, and snippets.

@osnr
Last active August 29, 2015 14:00
Show Gist options
  • Save osnr/c08434061c0ca4f218b1 to your computer and use it in GitHub Desktop.
Save osnr/c08434061c0ca4f218b1 to your computer and use it in GitHub Desktop.

Proposal: Outgoing Element ports and generalizing main

Let's allow outgoing ports to contain Elements, like this:

clicks : Input ()
clicks = Input ()

port takeClicks : Signal Element
port takeClicks = constant <| clickable clicks.handle () (spacer 40 40)

port showClicks : Signal Element
port showClicks = asText <~ count clicks.signal

The intent is that clicking on takeClicks on one part of the page will increment showClicks on some other part of the page.

OK, we need a JS subscription interface for these Element ports. Assume I've made divs #takeClicks and #showClicks somewhere on the page beforehand:

var clicker = Elm.worker(Elm.Clicker, {});
clicker.ports.takeClicks.subscribe(document.getElementById("#takeClicks"));
clicker.ports.showClicks.subscribe(document.getElementById("#showClicks"));

To subscribe to an Element port, you provide a DOM element into which the Elm runtime will render/merge updates, rather than a JS function to handle updates.

Where's main in our Elm, and how come we didn't use Elm.embed in our JS? Well, what do we need them for? main is just a special case of an Element port.

port main : Signal Element
port main = asText <~ constant "foo"

Actually, we can get rid of Elm.embed and Elm.fullscreen entirely. If you want to fullscreen an Element port, we could have a sentinel 'handler' like this:

worker.ports.display.subscribe(Elm.fullscreen);

Now the name main only matters if you're compiling from Elm to a self-contained HTML file (just like the C or Java main() entry points). If you're using Elm as a library embedded in HTML/JS, you can call your Element ports whatever you want, have as many as you want, and just subscribe divs to them.

This changes how we use the 'component model'. Rather than needing at least one module per viewport/component, and having them totally disconnected (unless you awkwardly glue them together from JS), we can now use a single Elm 'reactor' and have multiple viewports subscribed to parts of it. Little reactive windows in the wall of HTML, peeking into an underlying Elm program. Then we can minimize the amount of JS glue code we need to write and move as much logic into Elm as possible for many kinds of applications.

Some similar discussion on the favicon thread and the Elm-Test thread.

Potential problems / questions:

  • What happens to 'window-wide' things like Window.dimensions, Mouse.position, and Mouse.clicks if there is no longer one 'window' for a given worker?
  • Will this encourage bad/undesirable design, like using HTML/CSS for layout rather than exploring what Elm can do there?
  • How does this work for constant Element ports? Or ports which don't fire after you subscribe? Maybe we should force subscription on worker start?
  • Should we force people to specify a type signature for main now?
  • Especially if it needs a type signature, port main adds some more complexity for beginning users (at the very least, you now have to type port). Right now is also kind of confusing, though, because it's a special case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment