mdworld
mdworld

Reputation: 83

Refactoring update fuction in a basic Elm app

I've recently started with Elm and I run into a problem with the update function. My goal is to split up my big Main.elm file into multiple smaller files, but to do this I first try to split up the main components into smaller components in the same file. For this I rely heavily on this very informative guide.

It is fairly straightforward to split the Model and init (which I already did for DiceRoller) and it is trivial for the View. Not so much for the Update unfortunately.

Currently, it looks like this (in the master branch of the Main.elm file)

type Msg = Change String
    | Name String
    | Password String
    | PasswordAgain String
    | Roll
    | NewFace Int
    | SearchImages
    | NewSearchResult (Result Http.Error (List String))
    | ChangeTermInput String

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        Change newContent ->
            ({ model | content = newContent }, Cmd.none)
        Name name ->
            ({ model | name = name }, Cmd.none)
        Password password ->
            ({ model | password = password }, Cmd.none)
        PasswordAgain password ->
            ({ model | passwordAgain = password }, Cmd.none)
        Roll ->
            (model, Random.generate NewFace (Random.int 1 100))
        NewFace newFace ->
            ({ model | diceRoller = { dieFace = newFace} }, Cmd.none)
        SearchImages ->
            (model, getSearchResult model.termInput)
        NewSearchResult (Ok newResult) ->
            ( { model | termResult = newResult }, Cmd.none )
        NewSearchResult (Err _) ->
            (model, Cmd.none)
        ChangeTermInput term ->
            ({ model | termInput = term}, Cmd.none)

And I managed to get it a bit more refined, but this does not compile (also see this Main.elm in the refactoring branch):

type DiceRollerMsg = Roll
    | NewFace Int

type Msg = Change String
    | Name String
    | Password String
    | PasswordAgain String
    | MsgForDiceRoller DiceRollerMsg
    | SearchImages
    | NewSearchResult (Result Http.Error (List String))
    | ChangeTermInput String

updateDiceRoller : DiceRollerMsg -> DiceRoller -> DiceRoller
updateDiceRoller msg model =
    case msg of
        Roll ->
            model
        NewFace newFace ->
            { model | dieFace = newFace}

updateDiceRollerCmd : Msg -> Cmd Msg
updateDiceRollerCmd msg =
    case msg of
        Roll ->
            Random.generate NewFace (Random.int 1 100)
        NewFace newFace ->
            Cmd.none

updateCmd : Msg -> Model -> Cmd Msg
updateCmd msg model =
    Cmd.batch
        [ updateDiceRollerCmd msg
        , getSearchResult model.termInput
        ]

updateModel : Msg -> Model -> Model
updateModel msg model =
    case msg of
        Change newContent ->
            { model | content = newContent }
        Name name ->
            { model | name = name }
        Password password ->
            { model | password = password }
        PasswordAgain password ->
            { model | passwordAgain = password }
        MsgForDiceRoller msg ->
            { model | diceRoller = updateDiceRoller msg model.diceRoller}
        SearchImages ->
            model
        NewSearchResult (Ok newResult) ->
            { model | termResult = newResult }
        NewSearchResult (Err _) ->
            model
        ChangeTermInput term ->
            { model | termInput = term}

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    (updateModel msg model, updateCmd msg model)

It fails to compile with a type mismatch on the line Role in updateDiceRoller, because the pattern matches DiceRollerMsg, but it is trying to match Msg. If I just change the input and return types to DiceRollerMsg I get: Function updateDiceRollerCmd is expecting the argument to be: DiceRollerMsg But it is: Msg

Also I do not think that Cmd.batch in updateCmd is the best solution here.

I appreciate any input to making a better Elm app, also outside these questions.

Upvotes: 1

Views: 120

Answers (1)

Chad Gilbert
Chad Gilbert

Reputation: 36385

Your compile errors originate from using Msg as input and return values for updateDiceRollerCmd while the case statement is using DiceRollerMsg. You can fix this function by pattern matching from, and mapping to, MsgForDiceRoller.

updateDiceRollerCmd : Msg -> Cmd Msg
updateDiceRollerCmd msg =
    case msg of
        MsgForDiceRoller Roll ->
            Random.generate NewFace (Random.int 1 100)
                |> Cmd.map MsgForDiceRoller

        _ ->
            Cmd.none

There is one more compile error in your view where you will need to change onClick Roll to onClick (MsgForDiceRoller Rool)

Upvotes: 0

Related Questions