Search code examples
functional-programmingelm

Fold and map at the same time


I have a function

f : a -> b -> ( a, c )

and I need to apply f to a list of b, while a is accumulated and c is appended to a list, getting ( a, List c ). I think the signature of what I want to do is

(a -> b -> ( a, c )) -> a -> List b -> ( a, List c )

The real scenario here is that I have

getThing : Model -> Thing -> ( Model, Cmd Msg )

and need to run getThing on a list of Thing, passing the Model to each call to getThing and returning the model and all Cmds that are to be performed in a Platform.Cmd.batch.

I think this problem should be broken down into multiple parts, but I am not sure where to start. It feels like using a fold is appropriate for the Model, but I need a map for the Cmd part.


Solution

  • You just need to unpack the tuple returned by getThing in each iteration of the fold, then pack it back up with the command added to the accumulated list of commands as the accumulator.

    mapThings : (a -> b -> ( a, c )) -> a -> List b -> ( a, List c )
    mapThings getThing initialModel things =
        List.foldl
            (\thing ( model, cmds ) ->
                let
                    ( newModel, cmd ) =
                        getThing model thing
                in
                ( newModel, cmd :: cmds )
            )
            ( initialModel, [] )
            things
    

    The naming here is very specific to help mnemonics, but it can easily be generalized just by using more generic variable names.