Search code examples
randomcompositionelmmessage

Change widget model from parent in Elm


I am using the "compose" pattern in Elm.

In Main.elm, I am generating an initial seed for Random using the following:

type alias Model =
    { ...
    , seed : Seed
    }

initialModel : Model
initialModel =
    { ...
    , seed = initialSeed 0
    }

init : ( Model, Cmd Msg )
init =
    ( initialModel, generate InitializeSeed (int minInt maxInt) )

type Msg
    = ...
    | InitializeSeed Int

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

        InitializeSeed value ->
            ( { model | seed = Random.initialSeed value }, Cmd.none )

which seems to work well for initializing the random number generator seed to a random starting value.

I have an "independent" widget that is used in the main application. I would like to pass the seed down to that widget on receipt of the InitializeSeed message and then retrieve the value as it changes in the child widget (using Random.step) so that I can then update other widgets' seeds as they need the generator.

How can I generate a message for the child widget from the update function in Main so that I can pass the seed down to the child? How can the child return the updated seed back to the parent?

UPDATE:

I figured out how to pass the seed down to the child widget, although I am not certain that this is best way.

The child code looks like this:

type alias Model =
    { ...
    , seed : Seed
    }

replaceSeed : Model -> Seed -> Model
replaceSeed model seed =
    { model | seed = seed }

and in the parent, I modified the update function like this:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ...
        InitializeSeed value ->
            let
                seed =
                    Random.initialSeed value

                child_ =
                    Child.replaceSeed model.child seed
            in
                ( { model
                    | seed = seed
                    , child = child_
                  }
                , Cmd.none
                )

I am still looking for a way to have the child return the updated seed.


Solution

  • While the top level update function in any Elm program must be

    update : Msg -> Model -> (Model, Cmd Msg)
    

    there are no limitations on the shape of any of its descendents. So nothing prevents you defining for your child

    update : Seed -> Msg -> Model -> (Model, Cmd Msg, Seed)
    

    or, if the child never returns any commands

    update : Seed -> Msg -> Model -> (Model, Seed)
    

    Maybe you have several things to pass down, including for example a parent message. Then you could go as far as

    type alias Config msg = 
        { seed : Seed 
        , someParentMessage : String -> msg 
        }
    
    update : Config msg -> Msg -> Model -> (Model, Maybe (Cmd msg), Seed)
    

    You need to wire these all up appropriately in Main of course, but the compiler will help with that.