Writing A Word Memory Game In Elm - Part 3: Rethinking the Model
Tags: elm
Published: 2019-04-11 09:47:55

---
title: Writing A Word Memory Game In Elm - Part 3: Rethinking the Model
published: true
description: A tutorial for writing a nice word memory game in Elm
tags: elm
series: Writing A Word Memory Game In Elm 
cover_image: https://thepracticaldev.s3.amazonaws.com/i/zijfhkxcnplbo93mv7if.png
---

This is part 3 of the series "Writing A Word Memory Game In Elm", find:

* [Part 1: Setting Up an Elm Application With Parcel](https://dev.to/mickeyvip/writing-a-word-memory-game-inelm---part-1-setting-up-an-elm-application-with-parcel-1ppk)
* [Part 2: Modeling And Building a Basic Word Memory Game](https://dev.to/mickeyvip/writing-a-word-memory-game-inelm---part-2-modeling-and-building-a-basic-word-memory-game-583g)
* Part 3: Rethinking the Model
* [Part 4: Spicing Things Up With Randomness](https://dev.to/mickeyvip/writing-a-word-memory-game-in-elm-part-4-spicing-things-up-with-randomness-58ei)
* [Part 5 - More Randomness And More Game](https://dev.to/mickeyvip/writing-a-word-memory-game-in-elm-part-5-more-randomness-and-more-game-1i37)

---

The game works now. Cool. But looking at the current `Model`

```elm
type Word
    = SentenceWord ( Int, String )
    | HiddenWord ( Int, String )


type alias Words =
    List Word


type alias Model =
    { sentence : String
    , chosenWords : Words
    , chosenSentence : Words
    }
```

we can spot several problems:

* The `chosenSentence` and `chosenWords` are 2 separate parts of the `Model` and we can accidentally make them out of sync
* Since `chosenWords` is of type `Words`, it may contain also `SentenceWord`
* The `sentence` may also become out of sync with the rest of the `Model`
* We also have some code smell in `viewHiddenWord`:

```elm
viewHiddenWord : Word -> List Word -> Html msg
viewHiddenWord hiddenWord chosenWords =

            -- omitted code

            wordElement : Word -> Html Msg                
                wordElement word =
                    case word of
                        HiddenWord ( _, wordString ) ->
                            viewOption wordString

                        SentenceWord ( _, wordString ) ->
                            viewOption wordString

            -- omitted code
```

And probably more.

In Elm we want to invest additional time into a good planning of our model and in getting it as close as possible to a form that will eliminate the invalid state of our application.

This idea, not new I suppose, was introduced by Richard Feldman at Elm-Conf 2016 in his amazing (as always) [talk](https://www.youtube.com/watch?v=IcgmSRJHu_8): 

{% youtube IcgmSRJHu_8 %}

Since then we often hear _"make impossible states impossible"_ in different communities, not only Elm.

How can we improve our `Model` and eliminate possibilities for the invalid state and code smell?

Instead of having `chosenWords` and `sentenceWords` - we can just have `sentence` property and rename the `sentence` to `sentenceOriginal`:

```elm
type Word
    = SentenceWord ( Int, String )
    | HiddenWord ( Int, String )


type alias Words =
    List Word


type alias Model =
    { sentenceOriginal : String
    , sentence : Words
    }
```

Looks better, but now we lost the original sentence's word or player's input, since there is only 1 `String` placeholder. Well... we can have the player's input and the original word in the same type constructor:

```elm
type Word
    = SentenceWord ( Int, String )
    | HiddenWord ( Int, String, String ) -- index, answer, player's choice
```

Also, maybe instead of having an `index` that can also get out of sync, we can use `List.indexedMap` when rendering the `sentence` which will give us the index:

```elm
type Word
    = SentenceWord String
    | HiddenWord String String  -- answer, player's choice


type alias Words =
    List Word


type alias Model =
    { originalSentence : String
    , sentence : Words
    }
```

We can go even further into modeling and replace `String` with a concrete types:

```elm
type PlayerChoice =
    PlayerChoice String    


type Answer =
    Answer String    


type Word
    = SentenceWord String
    | HiddenWord Answer PlayerChoice


type alias Words =
    List Word


type alias Model =
    { originalSentence : String
    , sentence : Words
    }
```

This will prevent us from passing the answer (which is `String`) and the player's input (also `String`) to `HiddenWord`, in the wrong order. The code now is also more readable.

We have almost all we need now. The ability to have a random order of chosen words, however, is still missing. To solve this, we can add a `SortKey` to the `HiddenWord`:

```elm
type PlayerChoice =
    PlayerChoice String    


type Answer =
    Answer String    


type SortKey =
    SortKey Int


type Word
    = SentenceWord String
    | HiddenWord SortKey Answer PlayerChoice


type alias Words =
    List Word


type alias Model =
    { originalSentence : String
    , sentence : Words
    }
```

When the type constructor grows, it may be better to turn it to a record. Let's convert `HiddenWord` into a record:

```elm
type alias HiddenWord =
    { sortKey : Int
    , playerChoice : PlayerChoice
    , answer : Answer
    }
```

We don't need the `SortKey`, just `Int` is sufficient:

```elm
type PlayerChoice =
    PlayerChoice String    


type Answer =
    Answer String    


type alias HiddenWord =
    { sortKey : Int
    , playerChoice : PlayerChoice
    , answer : Answer
    }


type Word
    = SentenceWord String
    | HiddenWord HiddenWord


type alias Words =
    List Word


type alias Model =
    { originalSentence : String
    , sentence : Words
    }
```

At this point Elm will complain about 2 `HiddenWord` type definitions. 

![Compiler complains](https://thepracticaldev.s3.amazonaws.com/i/jl32fxkb7nh1q237igit.png)

We need to have different names. I renamed `SentenceWord` and `HiddenWord` to `SentenceWrd` and `HiddenWrd`:

```elm
type Word
    = SentenceWrd String
    | HiddenWrd HiddenWord
```

After this change we have a lot to refactor. Luckily we have our back covered with Elm compiler. Let's update the current `initialModel`:

```elm
type alias Model =
    { sentence : String
    , chosenWords : Words
    , chosenSentence : Words
    }


initialModel : Model
initialModel =
    { originalSentence = "The pen is mightier than the sword"
    , chosenWords =
        [ HiddenWord ( 1, "pen" )
        , HiddenWord ( 6, "sword" )
        , HiddenWord ( 3, "mightier" )
        ]
    , chosenSentence =
        [ SentenceWord ( 0, "The" )
        , HiddenWord ( 1, "" )
        , SentenceWord ( 2, "is" )
        , HiddenWord ( 3, "" )
        , SentenceWord ( 4, "than" )
        , SentenceWord ( 5, "the" )
        , HiddenWord ( 6, "" )
        ]
    }
```

to this:

```elm
type alias Model =
    { sentence : Words
    }


initialModel : Model
initialModel =
    { sentence =
        [ SentenceWrd "The"
        , HiddenWrd
            { sortKey = 1
            , answer = Answer "pen"
            , playerChoice = PlayerChoice ""
            }
        , SentenceWrd "is"
        , HiddenWrd
            { sortKey = 3
            , answer = Answer "mightier"
            , playerChoice = PlayerChoice ""
            }
        , SentenceWrd "than"
        , SentenceWrd "the"
        , HiddenWrd
            { sortKey = 2
            , answer = Answer "sword"
            , playerChoice = PlayerChoice ""
            }
        ]
    }
```

We hardcoded the "random" order of the chosen words by setting the `sortKey`, also removed the `originalSentence` from the model (for now) and renamed `chosenSentence` to `sentence`.

The `update` function is the first we can refactor easily. Let's change:

* `newSentence` to use `List.indexedMap`
* `updateWord` to take an index as the first argument

```elm
update : Msg -> Model -> Model
update msg model =
    case msg of
        WordChanged index wordString ->
            let
                updateWord : Int -> Word -> Word
                updateWord wordIndex word =
                    case word of
                        HiddenWrd hiddenWord ->
                            if wordIndex == index then
                                HiddenWrd
                                    { hiddenWord
                                        | playerChoice = PlayerChoice wordString
                                    }

                            else
                                word

                        _ ->
                            word

                newSentence : Words
                newSentence =
                    List.indexedMap updateWord model.sentence
            in
            { model | sentence = newSentence }
```

We now have an `index` of the word to update with the player's input, and `wordIndex` of the current word we are running `List.indexedMap` on. If both indexes are equal, we need to update the `playerChoice` field of the `HiddenWrd`.

Now there are a lot of compiler errors. Let's go step by step and comment out all the functions with errors, that is `viewSentence`, `viewHiddenWord`, `viewChosenWords` and the invocation of them in the `view`:

```elm
view : Model -> Html Msg
view model =
    main_ [ class "section" ]
        [ div [ class "container" ]
            [ viewTitle
            , div [ class "box" ]
                [ p
                    [ class "has-text-centered" ]
                    [ text model.sentence ]

                --                , viewSentence model.chosenSentence model.chosenWords
                --                , viewChosenWords model.chosenWords model.chosenSentence
                ]
            ]
        ]
```

The compilers complains about `text model.sentence` because `text` expects a `String` as input, but after the refactoring `model.sentence` is not a simple `String` but `Words` (which is itself a type alias for `List Word`).

First we need something that can convert a `Word` into a `String`:

```elm
wordToString : Word -> String
wordToString word =
    case word of
        SentenceWrd wordString ->
            wordString

        HiddenWrd hiddenWord ->
            case hiddenWord.answer of
                Answer answerString ->
                    answerString
```

If `word` is `SenteceWrd`, we'll take the `String` part of it. If it's the `HiddenWrd`, we'll take the `Answer` from it and then pattern match to get its `String` part.

Let's add a `viewOriginalSentence` function to render the sentence:

```elm
viewOriginalSentence : Words -> Html Msg
viewOriginalSentence words =
    p
        [ class "has-text-centered" ]
        (List.map
            (\word ->
                wordToString word |> text
            )
            words
        )
```

We are mapping each `Word` to a `String` and passing the `String` to `Html.text`.

Let's update the `view` function to use `viewOriginalSentence`:

```elm
view : Model -> Html Msg
view model =
    main_ [ class "section" ]
        [ div [ class "container" ]
            [ viewTitle
            , div [ class "box" ]
                [ p
                    [ class "has-text-centered" ]
                    [ viewOriginalSentence model.sentence ]

                --                , viewSentence model.chosenSentence model.chosenWords
                ]
                --                , viewChosenWords model.chosenWords model.chosenSentence
            ]
        ]
```

The application now compiles! Lets see how it looks:

![Original sentence created from our model](https://thepracticaldev.s3.amazonaws.com/i/13uiwawe2ktk4qm4136a.png)

Well... all the words are written without any spaces between them. We need to add some space, one way we can do it is by adding a space after each word:

```elm
viewOriginalSentence : Words -> Html Msg
viewOriginalSentence words =
    p
        [ class "has-text-centered" ]
        (List.map
            (\word ->
                wordToString word ++ " " |> text
            )
            words
        )
```

Or we can use [`List.intersperse`](https://package.elm-lang.org/packages/elm/core/latest/List#intersperse) that "_Places the given value between all members of the given list._":

```elm
viewOriginalSentence : Words -> Html Msg
viewOriginalSentence words =
    p
        [ class "has-text-centered" ]
        (words
            |> List.map wordToString
            |> List.intersperse " "
            |> List.map text
        )
```

or event simpler with [`String.join`](https://package.elm-lang.org/packages/elm/core/latest/String#join):

```elm
viewOriginalSentence : Words -> Html Msg
viewOriginalSentence words =
    p
        [ class "has-text-centered" ]
        [ words
            |> List.map wordToString
            |> String.join " "
            |> text
        ]
```


And now the game looks better:

![Original sentence created from our model with spaces between words](https://thepracticaldev.s3.amazonaws.com/i/r8a55sjxru5tc7qtb4gx.png)

Let's do a small refactor before moving on. Let's add `answerToString` helper function to convert `Answer` into a `String`. We can use it from `wordToString` and  it may come handy later when comparing the player's choice with the answer:

```elm
answerToString : Answer -> String
answerToString (Answer wordString) =
    wordString


wordToString : Word -> String
wordToString word =
    case word of
        HiddenWrd hiddenWord ->
            answerToString hiddenWord.answer

        SentenceWrd wordString ->
            wordString
```

Great. The next in line is the view for chosen words. Let's uncomment and examine it:

```elm
viewChosenWords : Words -> Words -> Html msg
viewChosenWords chosenWords sentenceWords =
    let
        viewChosenWord : Word -> Html msg
        viewChosenWord chosenWord =
            case chosenWord of
                HiddenWord ( _, wordString ) ->
                    let
                        isCorrectGuess : Bool
                        isCorrectGuess =
                            List.member chosenWord sentenceWords

                        className : String
                        className =
                            if isCorrectGuess then
                                "has-text-success"

                            else
                                "has-text-grey-light"
                    in
                    li []
                        [ span [ class className ]
                            [ text wordString
                            , text " "
                            , span [ class "icon is-small" ]
                                [ i [ class "far fa-check-circle" ] [] ]
                            ]
                        ]

                SentenceWord _ ->
                    text ""
    in
    ul [] (List.map viewChosenWord chosenWords)
```

The firs thing that we can improve is the signature. Now it takes `chosenWords` as `Words`, but it should only handle `List HiddenWord`.

Previously, we needed the `sentenceWords` to hint if the player's choice was correct. Now we have all the information in `HiddenWord`, both `playerChoice` and `answer`!

This means that the signature can be simplified and made to express the input better - we can only pass `List HiddenWord` here:

```elm
viewChosenWords : List HiddenWord -> Html msg
viewChosenWords chosenWords =
```

Let's also rename `viewChosenWords` to `viewHiddenWords` because `hidden` and `chosen` are used and it's confusing:

```elm
viewHiddenWords : List HiddenWord -> Html msg
viewHiddenWords hiddenWordList =
```

We also need a way to get the `List HiddenWord` from our model. Let's add this ability. We need to filter out the `HiddenWrd` and take `HiddenWord` from it (note "Word" vs. "Wrd"). And Elm has us covered with [List.filterMap](https://package.elm-lang.org/packages/elm/core/latest/List#filterMap):

```elm
hiddenWords : Words -> List HiddenWord
hiddenWords sentence =
    List.filterMap
        (\word ->
            case word of
                HiddenWrd hiddenWord ->
                    Just hiddenWord

                _ ->
                    Nothing
        )
        sentence
```

Now we can update `view` to call it:

```elm
view : Model -> Html Msg
view model =
    main_ [ class "section" ]
        [ div [ class "container" ]
            [ viewTitle
            , div [ class "box" ]
                [ p
                    [ class "has-text-centered" ]
                    [ viewOriginalSentence model.sentence

                    --                , viewSentence model.chosenSentence model.chosenWords
                    ]
                , viewHiddenWords (hiddenWords model.sentence)
                ]
            ]
        ]
```

Back to `viewHiddenWords`. It has an inner `viewChosenWord` function. Let's rename it to `viewHiddenWord` and change the signature, since we now have only `HiddenWord`: 

```elm
        viewHiddenWord : HiddenWord -> Html msg
        viewHiddenWord hiddenWord =
```

We don't need the `case` anymore for the same reason - we only have `HiddenWord` now, so we can delete it:

```elm
viewHiddenWords : List HiddenWord -> Html msg
viewHiddenWords hiddenWordList =
    let
        viewHiddenWord : HiddenWord -> Html msg
        viewHiddenWord hiddenWord =
            let
                isCorrectGuess : Bool
                isCorrectGuess =
                    List.member hiddenWord sentenceWords

                className : String
                className =
                    if isCorrectGuess then
                        "has-text-success"

                    else
                        "has-text-grey-light"
            in
            li []
                [ span [ class className ]
                    [ text wordString
                    , text " "
                    , span [ class "icon is-small" ]
                        [ i [ class "far fa-check-circle" ] [] ]
                    ]
                ]
    in
    ul [] (List.map viewHiddenWord hiddenWordList)
```

How do we know that the player's choice is the correct choice? As I previously mentioned, we have all we need in the `HiddenWord` type:

```elm
type alias HiddenWord =
    { sortKey : Int
    , playerChoice : PlayerChoice
    , answer : Answer
    }
```

All we need is to compare the `payerChoice` and `answer`. They both are container types:

```elm
type PlayerChoice
    = PlayerChoice String


type Answer
    = Answer String

```

So we need to extract the `String` value from them. Let's add `playerChoiceToString` in addition to a previously declared `answerToString`:

```elm
playerChoiceToString : PlayerChoice -> String
playerChoiceToString (PlayerChoice stringValue) =
    stringValue


answerToString : Answer -> String
answerToString (Answer stringValue) =
    stringValue
```

Pretty straightforward using the destructuring.

Now we can use them in `isCorrectGuess`:

```elm
viewHiddenWord : HiddenWord -> Html msg
        viewHiddenWord hiddenWord =
            let
                isCorrectGuess : Bool
                isCorrectGuess =
                    answerToString hiddenWord.answer == playerChoiceToString hiddenWord.playerChoice
```

The `className` needs no change. But the `li` declaration again needs a `String` where `wordString` was:

```elm
            li []
                [ span [ class className ]
                    [ text wordString
                    , text " "
                    , span [ class "icon is-small" ]
                        [ i [ class "far fa-check-circle" ] [] ]
                    ]
                ]
```

What should go there? The `String` from the `hiddenWord.answer` of course!

```elm
            li []
                [ span [ class className ]
                    [ text <| answerToString hiddenWord.answer
                    , text " "
                    , span [ class "icon is-small" ]
                        [ i [ class "far fa-check-circle" ] [] ]
                    ]
                ]
```

We can refactor it a little to reuse the value of `answerToString hiddenWord.answer` by adding 2 declarations:

```elm
viewHiddenWord : HiddenWord -> Html msg
        viewHiddenWord hiddenWord =
            let
                answerString : String
                answerString =
                    answerToString hiddenWord.answer
                    
                playerChoiceString : String
                playerChoiceString =
                    playerChoiceToString hiddenWord.playerChoice
```

and:

```elm
                isCorrectGuess : Bool
                isCorrectGuess =
                    answerString == playerChoiceString


-- omitted code

            li []
                [ span [ class className ]
                    [ text answerString
                    , text " "
                    , span [ class "icon is-small" ]
                        [ i [ class "far fa-check-circle" ] [] ]
                    ]
                ]
```

And now our `viewHiddenWords` looks like this:

```elm
viewHiddenWords : List HiddenWord -> Html msg
viewHiddenWords hiddenWordList =
    let
        viewHiddenWord : HiddenWord -> Html msg
        viewHiddenWord hiddenWord =
            let
                answerString : String
                answerString =
                    answerToString hiddenWord.answer

                playerChoiceString : String
                playerChoiceString =
                    playerChoiceToString hiddenWord.playerChoice

                isCorrectGuess : Bool
                isCorrectGuess =
                    answerString == playerChoiceString

                className : String
                className =
                    if isCorrectGuess then
                        "has-text-success"

                    else
                        "has-text-grey-light"
            in
            li []
                [ span [ class className ]
                    [ text answerString
                    , text " "
                    , span [ class "icon is-small" ]
                        [ i [ class "far fa-check-circle" ] [] ]
                    ]
                ]
    in
    ul [] (List.map viewHiddenWord hiddenWordList)
```

Lets look at our game:

![Game with hidden words](https://thepracticaldev.s3.amazonaws.com/i/abve6fjs8ohnkonfww5y.png)

Looks good, but we are missing one more detail - the hidden words are presented in the same order as they appear in the sentence... If you remember we added `sortKey` to our `HiddenWord` to be able to show them in random order:

```elm
type alias HiddenWord =
    { sortKey : Int
    , playerChoice : PlayerChoice
    , answer : Answer
    }
```

Currently, we have the random order hardcoded in the `initialModel`:

```elm
initialModel : Model
initialModel =
    { sentence =
        [ SentenceWrd "The"
        , HiddenWrd
            { sortKey = 3
            , answer = Answer "pen"
            , playerChoice = PlayerChoice ""
            }
        , SentenceWrd "is"
        , HiddenWrd
            { sortKey = 1
            , answer = Answer "mightier"
            , playerChoice = PlayerChoice ""
            }
        , SentenceWrd "than"
        , SentenceWrd "the"
        , HiddenWrd
            { sortKey = 2
            , answer = Answer "sword"
            , playerChoice = PlayerChoice ""
            }
        ]
    }
```

So what is left to do is to sort the hidden words by the `sortKey` before showing them. We'll add `hiddenWordsSorted` and use it :

```elm
viewHiddenWords : List HiddenWord -> Html msg
viewHiddenWords hiddenWordList =
    let
        hiddenWordsSorted : List HiddenWord
        hiddenWordsSorted =
            List.sortBy .sortKey hiddenWordList

        viewHiddenWord : HiddenWord -> Html msg
        viewHiddenWord hiddenWord =

-- omitted

    in
    ul [] <| List.map viewHiddenWord hiddenWordsSorted
```

![Randomly sorted hidden words](https://thepracticaldev.s3.amazonaws.com/i/6gpie62v25s3arb3p8mw.png)

One last small refactor I would like to do is to `wordToString`:

```elm
wordToString : Word -> String
wordToString word =
    case word of
        SentenceWrd wordString ->
            wordString

        HiddenWrd hiddenWord ->
            case hiddenWord.answer of
                Answer answerString ->
                    answerString
```

We have `answerToString` so we can use it in the second branch of the `case`:

```elm
wordToString : Word -> String
wordToString word =
    case word of
        SentenceWrd wordString ->
            wordString

        HiddenWrd hiddenWord ->
            answerToString hiddenWord.answer
```

or even shorter:

```elm
wordToString : Word -> String
wordToString word =
    case word of
        SentenceWrd wordString ->
            wordString

        HiddenWrd { answer } ->
            answerToString answer
```

The final part - `viewSentence`. Let's uncomment it in the `view` function.

```elm
            viewSentence model.chosenSentence model.chosenWords
```

We don't have `chosenSentence` anymore since we replaced `sentence`, and we also got rid of the additional `chosenWords`. We do need to send the `List HiddenWord` to the `viewSentence` so it knows what to render as a `<select>` element. We already have a helper function for it - `hiddenWords` - that we use when calling `viewHiddenWords`. So the `viewSentence` may look like this:

```elm
view : Model -> Html Msg
view model =

-- omitted
                    [ viewOriginalSentence model.sentence
                    , viewSentence model.sentence (hiddenWords model.sentence)
                    ]
```

Looks like we are sending `model.sentence` to `viewSentence` and using it to get hidden words... We don't need to send `(hiddenWords model.sentence)` since `viewSentence` can calculate them by itself!


```elm
view : Model -> Html Msg
view model =
    main_ [ class "section" ]
        [ div [ class "container" ]
            [ viewTitle
            , div [ class "box" ]
                [ p
                    [ class "has-text-centered" ]
                    [ viewOriginalSentence model.sentence
                    , viewSentence model.sentence
                    ]
                , viewHiddenWords (hiddenWords model.sentence)
                ]
            ]
        ]
```

Let's uncomment `viewSentence` and change its signature from:

```elm
viewSentence : Words -> Words -> Html Msg
viewSentence sentence chosenWords =
```

to

```elm
viewSentence : Words -> Html Msg
viewSentence sentence =
```

Now we'll obtain hiddenWords:

```elm
viewSentence : Words -> Html Msg
viewSentence sentence =
    let
        hiddenWords_ =
            hiddenWords sentence
    in
```

And update the rest of `viewSentence` to:

```elm
viewSentence : Words -> Html Msg
viewSentence sentence =
    let
        hiddenWords_ : List HiddenWord
        hiddenWords_ =
            hiddenWords sentence
    in
    div [ class "has-text-centered" ]
        (List.map
            (\sentenceWord ->
                case sentenceWord of
                    SentenceWrd word ->
                        span [ class "sentence-word" ] [ text word ]

                    HiddenWrd hiddenWord ->
                        span [ class "sentence-word" ] [ text (answerToString hiddenWord.answer) ]
             --                        viewHiddenWord sentenceWord hiddenWords_
            )
            sentence
        )
```

I have commented out the call to `viewHiddenWord` so we can see the intermediate result:

![Intermediate sentence render](https://thepracticaldev.s3.amazonaws.com/i/zcvmgquvgygtp88vm3c4.png)

Now let's uncomment `viewHiddenWord` and return the line inside `viewSentence`:

```elm
viewSentence : Words -> Html Msg
viewSentence sentence =
    let
        hiddenWords_ : List HiddenWord
        hiddenWords_ =
            hiddenWords sentence
    in
    div [ class "has-text-centered" ]
        (List.map
            (\sentenceWord ->
                case sentenceWord of
                    SentenceWrd word ->
                        span [ class "sentence-word" ] [ text word ]

                    HiddenWrd hiddenWord ->
                         viewHiddenWord sentenceWord hiddenWords_
            )
            sentence
        )
```

We need to change `sentenceWord` to `hiddenWord`:

```elm
viewSentence : Words -> Html Msg
viewSentence sentence =
    let
        hiddenWords_ : List HiddenWord
        hiddenWords_ =
            hiddenWords sentence
    in
    div [ class "has-text-centered" ]
        (List.map
            (\sentenceWord ->
                case sentenceWord of
                    SentenceWrd word ->
                        span [ class "sentence-word" ] [ text word ]

                    HiddenWrd hiddenWord ->
                         viewHiddenWord hiddenWord hiddenWords_
            )
            sentence
        )
```

Good. Now to `viewHiddenWord`. Let's update its type signature from:


```elm
viewHiddenWord : Word -> List Word -> Html Msg
viewHiddenWord hiddenWord chosenWords =
```

to

```elm
viewHiddenWord : HiddenWord -> List HiddenWord -> Html Msg
viewHiddenWord hiddenWord hiddenWords =
```

The only type that `viewHiddenWord` can accept now is `HiddenWord`. We also renamed all the `chosenXYZ` to `hiddenXYZ`.

Again, we don't need the `case` expression anymore!

```elm
viewHiddenWord : HiddenWord -> List HiddenWord -> Html Msg
viewHiddenWord hiddenWord hiddenWords =
    let
        viewOption : String -> Html Msg
        viewOption wordString =
            option
                [ value wordString, selected (wordString == hiddenWordText) ]
                [ text <| String.toLower wordString ]

        wordElement : Word -> Html Msg
        wordElement word =
            case word of
                HiddenWord ( _, wordString ) ->
                    viewOption wordString

                SentenceWord ( _, wordString ) ->
                    viewOption wordString
    in
    div [ class "select" ]
        [ select [ class "hidden-word", onInput (WordChanged hiddenIndex) ] <|
            option []
                [ text "" ]
                :: List.map wordElement hiddenWords
        ]
```

Let's also rename the 2 instances of `hiddenWords` to `hiddenWords_`, since the compiler complains about name collisions (2nd line and the line before the last in the code snippet).

```elm
viewHiddenWord : HiddenWord -> List HiddenWord -> Html Msg
viewHiddenWord hiddenWord hiddenWords_ =
    let
        viewOption : String -> Html Msg
        viewOption wordString =
            option
                [ value wordString, selected (wordString == hiddenWordText) ]
                [ text <| String.toLower wordString ]

        wordElement : Word -> Html Msg
        wordElement word =
            case word of
                HiddenWord ( _, wordString ) ->
                    viewOption wordString

                SentenceWord ( _, wordString ) ->
                    viewOption wordString
    in
    div [ class "select" ]
        [ select [ class "hidden-word", onInput (WordChanged hiddenIndex) ] <|
            option []
                [ text "" ]
                :: List.map wordElement hiddenWords_
        ]
```

The `wordElement` also gets simpler since it should only get `HiddenWord`, so the strange-looking `case` is gone:

```elm
viewHiddenWord : HiddenWord -> List HiddenWord -> Html Msg
viewHiddenWord hiddenWord hiddenWords_ =
    let
        viewOption : String -> Html Msg
        viewOption wordString =
            option
                [ value wordString, selected (wordString == hiddenWordText) ]
                [ text <| String.toLower wordString ]

        wordElement : HiddenWord -> Html Msg
        wordElement hiddenWord_ =
            viewOption (answerToString hiddenWord_.answer)
    in
    div [ class "select" ]
        [ select [ class "hidden-word", onInput (WordChanged hiddenIndex) ] <|
            option []
                [ text "" ]
                :: List.map wordElement hiddenWords_
        ]
```

The `viewOption` change is a pretty simple one. We need to select the `<option>` where the `answer` from the current `HiddenWord` equals `playerChoice` from the `HiddenWord` that was passed in:

```elm
viewHiddenWord : HiddenWord -> List HiddenWord -> Html Msg
viewHiddenWord hiddenWord hiddenWords_ =
    let
        playerChoiceString : String
        playerChoiceString =
            playerChoiceToString hiddenWord.playerChoice

        viewOption : String -> Html Msg
        viewOption answerString =
            option
                [ value answerString, selected (answerString == playerChoiceString) ]
                [ text <| String.toLower answerString ]

        wordElement : HiddenWord -> Html Msg
        wordElement hiddenWord_ =
            viewOption
                (answerToString hiddenWord_.answer)
    in
    div [ class "select" ]
        [ select [ class "hidden-word", onInput (WordChanged hiddenIndex) ] <|
            option []
                [ text "" ]
                :: List.map wordElement hiddenWords_
        ]
```

The compiler now complains about name collision between this `viewHiddenWord` and inner `viewHiddenWord` inside `viewHiddenWords`... Let's add `_` to the inner `viewHiddenWord`:

```elm
viewHiddenWords : List HiddenWord -> Html msg
viewHiddenWords hiddenWordList =
    let
        hiddenWordsSorted : List HiddenWord
        hiddenWordsSorted =
            List.sortBy .sortKey hiddenWordList

        viewHiddenWord_ : HiddenWord -> Html msg
        viewHiddenWord_ hiddenWord =

-- omitted

    in
    ul [] <| List.map viewHiddenWord_ hiddenWordsSorted

```

Looking at the code we can see that `wordElement` does almost nothing and just calls `viewOption`, so we can just remove it and call `viewOption` directly:

```elm
viewHiddenWord : HiddenWord -> List HiddenWord -> Html Msg
viewHiddenWord hiddenWord hiddenWords_ =
    let
        playerChoiceString : String
        playerChoiceString =
            playerChoiceToString hiddenWord.playerChoice

        viewOption : HiddenWord -> Html Msg
        viewOption hiddenWord_ =
            let
                answerString =
                    answerToString hiddenWord_.answer
            in
            option
                [ value answerString, selected (answerString == playerChoiceString) ]
                [ text <| String.toLower answerString ]
    in
    div [ class "select" ]
        [ select [ class "hidden-word", onInput (WordChanged hiddenIndex) ] <|
            option []
                [ text "" ]
                :: List.map viewOption hiddenWords_
        ]
```

We now pass `HiddenWord` to `viewOption` and calculate `answerString` inside it. We also can use destructuring in the `viewOption` declaration to get `hiddenWord_.answer` immediately:

```elm
        viewOption : HiddenWord -> Html Msg
        viewOption { answer } =
            let
                answerString : String
                answerString =
                    answerToString answer
            in
            option
                [ value answerString, selected (answerString == playerChoiceString) ]
                [ text <| String.toLower answerString ]
```

We still have `hiddenIndex` in our `viewHiddenWord` that we haven't handled. This should be the index of the word in the sentence, so we can know what word to update with the player's choice. As we discussed in the beginning of the refactor, we can have this index by using `List.indexedMap` instead of `List.map` in `viewSentence`,  and call `viewHiddenWord` with that index.

Let's update `viewHiddenWord` to use `List.indexedMap`, get the index in the inner function and pass it to `viewHiddenWord`:

```elm
viewSentence : Words -> Html Msg
viewSentence sentence =
    let
        hiddenWords_ : List HiddenWord
        hiddenWords_ =
            hiddenWords sentence
    in
    div [ class "has-text-centered" ]
        (List.indexedMap
            (\index sentenceWord ->
                case sentenceWord of
                    SentenceWrd word ->
                        span [ class "sentence-word" ] [ text word ]

                    HiddenWrd hiddenWord ->
                        viewHiddenWord index hiddenWord hiddenWords_
            )
            sentence
        )
```

Now let's update `viewHiddenWord` to take the index as its first argument and use it:

```elm
viewHiddenWord : Int -> HiddenWord -> List HiddenWord -> Html Msg
viewHiddenWord index hiddenWord hiddenWords_ =

-- omitted 

    in
    div [ class "select" ]
        [ select [ class "hidden-word", onInput (WordChanged index) ] <|
            option []
                [ text "" ]
                :: List.map viewOption hiddenWords_
        ]
```

The game compiles and works!

But the `<option>`s in the drop down again come in the same order as they appear in the sentence. We still need to sort them by `sortKey` as we did in `viewHiddenWords`.

We are repeating ourselves with `hiddenWordsSorted`, so lets refactor it to its own function:


```elm
hiddenWordsSorted : List HiddenWord -> List HiddenWord
hiddenWordsSorted hiddenWordList =
    List.sortBy .sortKey hiddenWordList
```

and use it inside `viewHiddenWords`:

```elm
viewHiddenWords : List HiddenWord -> Html msg
viewHiddenWords hiddenWordList =
    let
        hiddenWordsSorted_ : List HiddenWord
        hiddenWordsSorted_ =
            hiddenWordsSorted hiddenWordList

--- omitted

    in
    ul [] <| List.map viewHiddenWord_ hiddenWordsSorted_
```

and in `viewHiddenWord`:

```elm
viewHiddenWord : Int -> HiddenWord -> List HiddenWord -> Html Msg
viewHiddenWord index hiddenWord hiddenWords_ =
    let
        hiddenWordsSorted_ : List HiddenWord
        hiddenWordsSorted_ =
            hiddenWordsSorted hiddenWords_

-- omitted

    in
    div [ class "select" ]
        [ select [ class "hidden-word", onInput (WordChanged index) ] <|
            option []
                [ text "" ]
                :: List.map viewOption hiddenWordsSorted_
        ]
```

And now we have the hidden words in the correct random order also in the drop down:

![Hidden words in the correct order in the drop down](https://thepracticaldev.s3.amazonaws.com/i/8efg82y1pfcean99jmp1.png)

Phew! That was a lot! And we gained a lot! Much less places to get our code into invalid state.

The current progress is saved in the repo under a Tag v2.0 https://github.com/mickeyvip/words-memory-game/tree/v2.0.

In the next post we will add randomness to select different words for each new game. Stay tuned!


## Acknowledgements

Special thanks to Joël Quenneville for reviewing the code and suggesting the refactoring presented in this post.