Rupus Reinefjord
Rupus Reinefjord

Reputation: 23

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.

Upvotes: 2

Views: 429

Answers (2)

genthaler
genthaler

Reputation: 73

If you don't mind pulling in another package, there's elm-community/list-extra List.Extra.mapAccuml and List.Extra.mapAccumr

https://package.elm-lang.org/packages/elm-community/list-extra/latest/List-Extra#mapAccuml

Upvotes: 1

glennsl
glennsl

Reputation: 29106

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.

Upvotes: 5

Related Questions