# Rethinking Maybes for Elm beginners

# Beginners will use Maybe incorrectly

Elm was basically my first programming language. When I first started I wanted to use a `Maybe a`

everywhere. But using a “Maybe” where you don’t need it can cause a lot of headaches down the road. Lets look an little example from an app I recently made for a flashcard.

# Example Problem

I need a tool to help me practice greek flash cards. Greek nouns have a gender (Masculine, Feminine, Neuter), a number (singular, Plural) and a case which is an ending that indicates how it is being used in the sentence (Nominative, Genitive, Dative, Accusative, Vocative).

I want an app to help me select all of these buttons on the flashcard. You can embed HTML and javascript into an Anki flashcard so I decided to build this myself.

So I started out using a model like:

I decided Maybe one of each would be selected. And creating a type for each option. I’m going to keep this simple and start with only Gender for views and updates. When I click the buttons I can see model below change.

Now to add the color. We will need handle a two cases for every button. One option would be to proceed straightforwardly.

we would need to fill in for each Gender where the `_`

would go. But We would quickly run into a problem. We would need to handle combinations of all possible models.

But this logic is wrong. My code following this path led me to this:

This ends up being very wrong. If you use a Maybe, you only have to two cases `Just a ->`

and `Nothing ->`

The `Just Masculine ->`

could easily have been written as `Just whatever_lol_`

.

We solve this by adding another case statement below the the `Just`

.

Now this is ok for one button. But lets see what happens with another set of options, this time for “number”.

This is the code for adding Number to just the Masculine branch of the function. We add indentation 8 levels deep. We can not realistically add more complexity to this app.

The primary reason being that we are using a `Maybe`

where we shouldn’t be. If we look at where `Maybe`

appears in the core library we can find something out. Let’s focus on List

`-- Under Utilities`

head : List a -> Maybe a

tail : List a -> Maybe (List a)

-- Under Deconstruction

maximum : List comparable -> Maybe comparable

minimum : List comparable -> Maybe comparable

`Maybe`

is used in core libraries with deconstruction. In the `List`

examples we see that

`head`

deconstructs the first list into one value`tail`

gives a subset of the original list`maximum`

gives the largest value of the list`minimum`

gives the smallest value of the list

In all of these cases the functions need to account for a list being empty `[]`

the empty list has no minimum, no maximum, no head, no tail. Thats where maybe comes in. These functions need to return something. So the functions return a value or `Nothing`

.

# bringing it together

the solution to our problem will obviously be to reduce our use of `Maybe`

. `Maybe`

does not fit our case, since we are deconstructing anything or converting. We are making a selection. Our possible selections absolutely should be of the same type. So the solution I came up with for this problem is to use a `No<SELECTED_TYPE>`

type. Like `type Number = NoNumber | Singular | Plural`

and below:

`Maybe is used in core libraries with deconstruction. In the List examples we see that`

`head`

deconstructs the first list into one value`tail`

gives a subset of the original list`maximum`

gives the largest value of the list`minimum`

gives the smallest value of the list

In all of these cases the functions need to account for a list being empty `[]`

the empty list has no minimum, no maximum, no head, no tail. Thats where maybe comes in. These functions need to return something. So the functions return a value or `Nothing`

.

# bringing it together

the solution to our problem will obviously be to reduce our use of `Maybe`

. `Maybe`

does not fit our case, since we are deconstructing anything or converting. We are making a selection. Our possible selections absolutely should be of the same type. So the solution I came up with for this problem is to use a `No<SELECTED_TYPE>`

type. Like `type Number = NoNumber | Singular | Plural`

and below:

type alias Model =

{

selectedGender : Gender

, selectedNumber : Number

, selectedCase : Case

}type Number = NoNumber | Singular | Plural

type Gender = NoGender | Masculine | Feminine | Neuter

type Case = NoCase | Nominative | Genitive | Dative | Accusative | VocativeinitialModel : Model

initialModel =

{ selectedGender = NoGender

, selectedNumber = NoNumber

, selectedCase = NoCase

}

I posted my full solution below. A lot of the nested complexity is scaled back, not abstracted away. With a lot of creativity, we are able to handle with one function and one `if ... else`

statement inside that function.

But we owe all this to getting rid of the `Maybe`

. At first the possible states were:

- {gender = Nothing, number= Nothing, case=Nothing}
- {gender=GenderType, number= Nothing, case=Nothing}
- {gender = Nothing, number= NumberType, case=Nothing}
- {gender = Nothing, number= Nothing, case=CaseType}
- {gender=GenderType, number= NumberType, case=Nothing}
- {gender=GenderType, number= Nothing, case=CaseType}
- {gender = Nothing, number= NumberType, case=CaseType}
- {gender = GenderType, number= NumberType, case=CaseType}

But with by making a `No<SELECTED_TYPE>`

option, We cut this down to one possible state.

- {gender = GenderType, number= NumberType, case=CaseType}

# Epilogue

To reiterate. Maybe is for deconstruction. But … the above example doesn’t tell the whole story. The view function doesn’t need to be nested as deep as possible. Using Maybe’s doesn’t necessarily make code wider/deeper. The code below and linked in full shows how the code can be of similar size and complexity. Its unlikely we could have come to this conclusion of the starting with using Maybes rather than the NotSelected types.

type alias Model =

{ selectedGender : Maybe Gender

, selectedNumber : Maybe Number

, selectedCase : Maybe Case

}viewButtonColor model selection message tup =

if Just (Tuple.first (Tuple.first tup)) == selection then

button

[ style "background-color" (Tuple.first (Tuple.second tup))

, style "color" (Tuple.second (Tuple.second tup))

]

[ text (Tuple.second (Tuple.first tup)) ]else

button [ onClick (message (Tuple.first (Tuple.first tup))) ]

[ text (Tuple.second (Tuple.first tup)) ]