Skip to content

Instantly share code, notes, and snippets.

@rtfeldman
Forked from mgold/proposal.elm
Last active October 7, 2015 22:32
Show Gist options
  • Save rtfeldman/932be43451818926fca6 to your computer and use it in GitHub Desktop.
Save rtfeldman/932be43451818926fca6 to your computer and use it in GitHub Desktop.
Elm mailbox revamp

This is proposed as a simplified alternative to https://gist.github.com/mgold/f3527359996fdf295843 - what if instead of renaming Mailbox, we just didn't have a name for that thing?

Rationale:

  1. From an API perspective, it's important that the function for instantiating a Signal returns two distinct values: the Signal and a way to send a Message to it.
  2. Mailbox (and alternatively Dispatcher) give a type alias to that return value.
  3. In practice, we never compose Mailboxes or Dispatchers directly; we simply read the two values out of them and then immediately discard them.
  4. Despite this, I hear people saying things like "Do I need a Mailbox for that?" when the real question is "I want to create a Signal; how do I do that?" The type alias seems to be a red herring that leads people down the wrong path.
  5. I've gotten the best results from telling people "Focus on the Signal. Do you need to create a new Signal out of thin air? If so, here's the function that does that." (Currently that function is called Signal.mailbox, which people then have to memorize.)
  6. We could embrace this by removing the type alias and instead have the Signal creation function use the standard approach for returning two things: have it return a tuple.

The specific proposal, relative to the 0.16 Signal API:

1. Remove Signal.Address and Signal.Mailbox

2. Replace Signal.mailbox with Signal.create, which simply returns a tuple:

create : a -> (Signal a, a -> Message)

3. See Max's gist for the refactors related to Address being gone.

Example

In practice, Mailbox is essentially always used like so:

openDatepickerMailbox =
    Signal.mailbox ()

...

port openDatepicker : Signal ()
port openDatepicker =
    openDatepickerMailbox.signal
    
...

view openDatepickerMailbox.address actions model

In this tuple world, we would instead do:

(openDatepicker, sendToOpenDatepicker) =
    Signal.create ()

...

port openDatepicker : Signal ()
port openDatepicker =
    openDatepicker
    
...

view sendToOpenDatepicker actions model

Drawback: Toplevel Annotation

As far as I know, there is currently no way to add a type annotation to a destructured tuple. Using this style would thus presumably lead to an unresolvable warning for a missing toplevel annotation.

With the caveat that any new language feature imposes a burden, it does seem like allowing destructured type annotations would be useful and intuitive anyway, so I would propose introducing the following:

(openDatepicker : Signal (), sendToOpenDatepicker : () -> Message) =
(openDatepicker, sendToOpenDatepicker) =
    Signal.create ()

Just a thought.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment