2

As a learning example for elm I want to create a simple snake game with the elm architecture.

Here is the full code so far: https://gist.github.com/DanEEStar/b11509514d72eaafb640378fc7c93b44

A part of the program is a UpdateWorld message which gets generated by a button click and a updateWorld function which is called when the user presses the space key.

This leads to the following compiling and working code (snippets form the full code):

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

        KeyPress keyCode ->
            ( handleInput keyCode model, Cmd.none )


handleInput : Int -> Model -> Model
handleInput keyCode model =
    case Char.fromCode keyCode of
        ' ' ->
            updateWorld model

        _ ->
            model


updateWorld : Model -> Model
updateWorld model =
    { model | snake = updateSnake model.snake }


subscriptions : Model -> Sub Msg
subscriptions model =
    Keyboard.presses KeyPress


view : Model -> Html Msg
view model =
    div []
        -- here I can simply tell `onClick` to generate the `UpdateWorld` command
        [ button [ onClick UpdateWorld ] [ text "tick" ]
        ]

In this code snippets it is very clear that the onClick event generates the UpdateWorld command. But in the handleInput function I have to "manually" call the updateWorld function.

What I would rather do is "generate" a new UpdateWorld command from my handleInput function. I think this would clarify the code. Something like:

handleInput keyCode =
    case Char.fromCode keyCode of
        ' ' ->
            -- how can I generate this command in my code
            UpdateWorld

How could I do this?

Is this even a good idea, or what would be a better pattern?

DanEEStar
  • 6,140
  • 6
  • 37
  • 52

1 Answers1

5

Recall that update is just a function, which means you can call it recursively.

Consider changing the signature of handleInput to also return a (Model, Cmd Msg), just like the update function:

handleInput : Int -> Model -> (Model, Cmd Msg)
handleInput keyCode model =
    case Char.fromCode keyCode of
        ' ' ->
            update UpdateWorld model
        'a' ->
            { model | snake = changeSnakeDirection model.snake West } ! []
        ...

You can get rid of the updateWorld function and move that code to the UpdateWorld message handler:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        UpdateWorld ->
            ( { model | snake = updateSnake model.snake }, Cmd.none )

        ChangeDirection newDirection ->
            ( { model | snake = changeSnakeDirection model.snake newDirection }, Cmd.none )

        KeyPress keyCode ->
            handleInput keyCode model
Chad Gilbert
  • 36,115
  • 4
  • 89
  • 97
  • 1
    Thanks, this exactly solves my problem. I was looking into the wrong corner. – DanEEStar May 25 '17 at 10:13
  • I have to take away the acceptance flag. After thinking some more about the problem I am still interested in the original question... – DanEEStar May 25 '17 at 21:40
  • I stumbled into this answer of you: https://stackoverflow.com/a/42703561/669561. This seems to be the same problem I have?! – DanEEStar May 25 '17 at 21:44
  • 3
    Yes, as explained in that answer, while it is possible to fire off another `Cmd` for a specific `Msg`, you can/should stick with just calling `update` recursively unless you have an explicit reason for wanting to go through another Elm Architecture event cycle. – Chad Gilbert May 25 '17 at 22:10