Elm 0.17 and the “end of FRP”

Probably the biggest change in elm in 0.17 is the removal of the Signal type, and the inclusion of a builtin event loop for the main module, as well as an implied event loop for each other “effect module”.

Elm always differed from other less rigorous FRP systems in that there is a hard world step in which the entire app state is recomputed by a single evaluation. This means that nothing is lost by moving to a builtin scheduler for the main module, because evaluation always worked that way, one path or another.

But didn’t signals decouple things from each other?

Not really: in order to affect application execution, they needed to be explicitly included in the app’s inputs list, part of the static signal graph feeding main at construction time. If you try to send a message into a signal, you’ll notice that a Task comes out, which promises to send the message at an appropriate time, but as a beginner, what do you do with it? "Why doesn’t it run in my program?"

If you asked these questions you would quickly have been guided to the tasks port, a port whose type was Signal of Task, only declarable in the main module, and recognized by the runtime to receive the special job of actually running tasks, thereby feeding the queue for the application’s input signal. You were not allowed to implement this yourself. In a sense, you didn’t have a choice about the implied event loop of the application, which was always a part of the runtime, but you had the boilerplate and the ability to get it wrong.

Elm 0.17 makes this a core part of the environment.

At an application level, not much has changed.

You emit effects containing tasks from a module’s update and they emit messages back to the module’s event loop when a state change is required.

Along with this change, is a significant new capability for elm modules in libraries; the ability to maintain their own event loop and process their own effects. One might envision this change as the direct analog of allowing a tasks port to appear in an imported module, but now instead there’s a new module type that both declares and encapsulates this functionality.

Something else that elm 0.17 makes easy that used to be pretty complicated is idempotent registration for global events and side effects that are sometimes necessary on the real web.

Focusing inputs, triggering file uploads, and playing sounds all use web API calls that must be called on the same stack as a call to a native event handler. If you wrote imperative code to attach these events in javascript, you also needed elm code to be savvy about when to send the effect, requiring more state. Elm manages subscription changes, so a module managing stateful operations can also take over this responsibility. You won’t have to build this incidental complexity into the main app state anymore.

If there’s anything that strictly speaking leaves FRP, it’s this and used judiciously, it’ll make programs simpler and easier to reason about.