Jiri Knesl
Jiri Knesl

Reputation: 225

How to start effect from update function?

I declared update function:

update : Action -> Model -> (Model, Effects.Effects Action)

And my model is:

model = Signal.foldp update init actions.signal

But Signal.foldp expects update function with Action -> Model -> Model shape.

Effect is POST call with code:

Http.post Json.Decode.string "http://localhost/tracker/link-admin/test.php" body 
|> Task.toMaybe 
|> Task.map SetShortenedUrl 
|> Effects.task

I have two questions:

  1. how can I run effect from update?
  2. how can I change my model to type check?

Thanks!

Upvotes: 0

Views: 100

Answers (1)

Chad Gilbert
Chad Gilbert

Reputation: 36375

If you're going to be using Effects without StartApp, at some point you'll need to use Effects.toTask, which requires a mailbox that can accept a list of actions.

Note that the documentation of Effects.toTask warns away from its explicit use except by expert hands (full disclosure: I am no expert) and at most once per app, but I'm going to pick it apart here anyways as an exercise in understanding StartApp.

Using the StartApp source code as our guide, we can pick out just the bare minimum we need to get your example running. First off, we have to deal with the pesky mailbox problem by defining a second mailbox called messages that takes a list of actions.

messages =
  Signal.mailbox []

Now, we have to redefine your actions mailbox in terms of this new messages mailbox, using only the first element of the list, or NoOp in case of an empty list. (Read up on Effects.toTask to understand why it is a list, and why we're ok in this example to treat it as a singleton list)

actions : Signal.Mailbox Action
actions =
  let
    singleton action =
      [action]
    fromList list =
      case list of
        [] -> NoOp
        (action::_) -> action
  in
    { address = Signal.forwardTo messages.address singleton
    , signal = Signal.map fromList messages.signal
    }

We'll need a new Signal to deal with Effects so we can split out the processing in the update function from the tasks which we'll soon send to a port. Again, picking snippets from StartApp, we can define the following signal

effectsAndModel : Signal (Model, Effects.Effects Action)
effectsAndModel =
  let
    updateJustModel action (model,_) = update action model
  in
    Signal.foldp updateJustModel init actions.signal

Now that we have the above effectsAndModel signal, we can now redefine your model signal by just peeling off the Model from the first element of the tuple.

model : Signal Model
model =
  Signal.map fst effectsAndModel

Likewise, we can use the effectsAndModel signal to send the tasks out a port by peeling off the Effects from the second element of the tuple:

port tasks : Signal (Task.Task Effects.Never ())
port tasks =
  Signal.map (Effects.toTask messages.address << snd) effectsAndModel

The above port, tasks is where the Effects.toTask happens which required the redefining of your actions mailbox.

I've created a simple working example with an ad hoc view method to verify your requirements at this gist. I changed it to look for a local test.txt file rather than your url.

Upvotes: 1

Related Questions