Composing a Task with a function that returns a Cmd

Say I have a function doSomething : a -> Cmd b and another getArgument : Task x a. Can I combine them to get a single Cmd b?

Classic example would be getting current time with Time.now and passing it to a function Time.Posix -> Cmd b. In my case I want to use timestamp in the name of a file downloaded with File.Download.string.

If doSomething would return a Task x b I could do

getArgument 
    |> Task.andThen doSomething 
    |> Task.attempt handleResult

Is there a way to compose the Task and a function returning a Cmd? Or do I always need two Msgs an two updates - first to get a and then to pass it to the doSomething and return a Cmd b?

You could write a “Helper-message” that could look like this:

type Msg =
   ...
   | GetTimeFor (Time.Posix -> Msg)
   | SaveToFile String Time.Posix

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
   case msg of
      ...
      GetTimeFor toMsg->
         (model, Task.perform toMsg Time.now)

Didn’t check if this compiles, but does it make sense? :sunny:

you would send the message like GetTimeFor (SaveToFile "hello bro")

I think it’s quite nice that GetTimeFor is now reusable wherever you may need it!

2 Likes

Neat idea @opsvager, I had never considered a recursive Msg type like that before. :+1:

1 Like

Note that there are drawbacks to storing functions in your Msg type (same problems also apply to the Model type) Implications of storing functions in model or msg type

2 Likes

Agreed - these kinds of tricks comes at a cost. Alternatively, you could make a specialized GetTimeForSaveFile-message, without the type-parameter. It feels “dumber”, but it’s probably the best approach :sunny:

A similar but slightly different approach

init : () -> ( Model, Cmd Msg )
init _ =
    ( ()
    , Time.now
        |> Task.map timeToCmd
        |> Task.perform RunCmd
    )


type Msg
    = RunCmd (Cmd Msg)
    | ...


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        RunCmd cmd ->
            ( model, cmd )
        ...

Someone can correct me if I’m wrong about this but I believe storing Cmd or Task in your Msg/Model has the same drawbacks as storing functions.

I believe most likely it does. Cmd.none == Cmd.none does work but trying to compare commands in general will result in an error.

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.