Skip to content

Instantly share code, notes, and snippets.

@evancz
Last active May 21, 2017 05:01
Show Gist options
  • Save evancz/44c90ac34f46c63a27ae to your computer and use it in GitHub Desktop.
Save evancz/44c90ac34f46c63a27ae to your computer and use it in GitHub Desktop.

Components with Effects

One of the big questions we see these days is “how do I use the Elm Architecture with tasks?”

So the goal here is to outline how to have components nested deep in your application that can trigger effects.

Overall Picture

So the overall picture is that we have a pure and immutable chunk of code called “the Elm Architecture” where we describe our whole application. We then have tiny services on the borders where all of our effects are run and then routed back into our application.

services

A number of nice benefits come from this pattern. They show up at least in the case of HTTP communitation and database communication.

  • HTTP: Let's say you are loading a bunch of information, possibly redundant in various ways. In the normal world, every component sends a bunch of requests independent of everything else, so if 10 things need your profile picture, they all send that request. By routing all the HTTP effects to a centlized place, we can ensure that these are batched, perhaps making things much faster.

  • Databases: Let's say a bunch of components generate changes they'd like to send to the database. It is concievable that you want to turn a bunch of haphazard changes into a single transaction that will succeed or fail in a way that is easy to understand and work with. By batching all the database effects in one place, there can be some logic about how to batch things into transactions, or perhaps avoid sending the requests altogether under certain circumstances.

While it is nice that you actually can get some nice things from this setup, it really is not a choice for us. Ultimately, we have to do things this way if we want to continue to manage effects. So the question is effectively, how do make this happen in a regular and easy to understand way.

Basic Version of an Effectful Architecture

This essentially extends the basic Elm Architecture with some way of threading effects around.

-- BASIC STUFF

model : Model

view : Address Action -> Model -> Html

update : Address Action -> Action -> Model -> (Model, Effects)
-- We get an address in so we know how to route any effects


-- EFFECTS

type Effects
  -- An opaque type, conceptually a list of tasks.
  -- It can be constructed in very specific ways.


doNothing : Effects


arbitraryTask : Address action -> Task Never action -> Effects
-- When the `Task` is done, the result is routed to the given `Address`.
-- `Never` is an uninhabited type, no value exists that has that type.
-- See http://package.elm-lang.org/packages/imeckler/empty/latest
-- The point is that we can specify "this task must not fail" which is
-- another way of saying "you must explicitly handle any potential errors"


http : Address action -> Http Never action -> Effects
-- `Http` is some custom thing like `Task`, but with extra restrictions
-- By making it its own thing, we can analyze it to automatically do any
-- necessary batching and caching.


database : Address action -> Database Never action -> Effects
-- `Database` is the same idea as `Http` where we can have some custom
-- chainable thing that can be turned into atomic transactions in a nice way


batch : List Effects -> Effects
-- Sometimes you want to do a batch of things all at once.


-- START

start : App model action -> Output

type alias App model action =
    {
        model : model,
        view : Address action -> model -> Html,
        update : Address action -> action -> model -> (model, Effects)
    }

type alias Output =
    {
        frames : Signal Html,
          -- hand this to main
        effects : Signal (Task () ())
          -- hand this directly to a port, it will run effects and route the results
    }

Now I think the two main concerns here are:

  1. Will it be a pain to manually route Effects? If I am updating the subcomponents, I need to be sure to gather all their effects together and say something like batch subeffectsin my update function.
  2. It seems like the ideal version of this will allow people to write custom effect managers. If you want to integrate with IndexDB or MySQL or whatever, maybe you want to write a custom thing just for that. I think this is doable, but I don't have enough experience yet to be able to write down what that'll look like.
@aphorisme
Copy link

After some days off, I had another look into my question which Max pointed out to be related to this gist.

After all, I've came up with a general idea I put into a library; maybe it can give you some inspiration on your thoughts: (https://github.com/aphorisme/elm-oprocesso)
It's very fresh, but I will use it on my next apps, so it might evolve into something helpful.

@aphorisme
Copy link

Just in case you already had a look: I've decided to change the main type, such that one can glue actions together. I'm on the run for now, but I will finish it tomorrow (or later).

@rgrempel
Copy link

@mgold I did indeed eventually have to supply the model to the reaction function -- as you predicted, I came across a use case that needed it.

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