# Staying sane with Maybe (maybe vs type)

**URL:** <https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897>\
**Category:** Learn\
**Created:** [July 23, 2024, 9:16pm UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897 "2024-07-23T21:16:46Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![badlydrawnrob](https://avatars.discourse-cdn.com/v4/letter/b/ee59a6/32.png) [@badlydrawnrob](https://discourse.elm-lang.org/u/badlydrawnrob)\
**Post date:** [July 23, 2024, 9:16pm UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/1 "2024-07-23T21:16:46Z")

</div>

I’m getting used to `Maybe` when loading in `json` from the server, working through [Programming Elm](https://pragprog.com/titles/jfelm/programming-elm/)’s `Photo` type, which is a big record that might not exist in the `json`.

I’m feeling pretty comfortable `case`ing on `Just` or `Nothing` which needs to happen a few times in `Msg` `update` branches (we need to unpack and rewrap it in a `Just` to store in our `Model`). It uses [`Maybe.map`](https://package.elm-lang.org/packages/elm/core/latest/Maybe#map) with an `updateRecordField` helper function and the `maybePhoto` to do the business.

`Maybe.withDefault` is also there to make things easier, but it seems to be for more primitive data types (creating an empty or default `Photo` record seems pointless).

I’ve got two questions:

1. **It’s sensible to have your main data point as a `Maybe` in case it’s not there** , but silly to have `Maybe`s cluttering up the place.
  - If you _don’t_ have control of the data (user input) how do you normally go about controlling your `optional` or `required` data points, so if they’re missing things don’t screw up?
  - If you _do_ have control of your data (admin input) a `Maybe` seems unnecessary if you’re careful, right?

2. **[`Maybe.withDefault`](https://package.elm-lang.org/packages/elm/core/latest/Maybe#withDefault) has a note about not overusing it**, and it might be better to use a custom type instead.
  - Are there any good videos or tutorials of a refactor you know of that shows the wrong and right way to do things? Simple explanations if possible.

[This post](https://thoughtbot.com/blog/maybe-mechanics), for example shows a `User` that has a `List friend` and requires `List.head` to edit a friend. `List.head` also returns a `Just a` so you’re unpacking two `Maybe`s there.

- How do you keep sane and the code simple?
- Where do you avoid using a `Maybe` type?

Thanks in advance! 🙂

---

<div class="post-metadata">

**Author:** ![mika](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mika/32/616_2.png) [@mika](https://discourse.elm-lang.org/u/mika)\
**Post date:** [July 23, 2024, 10:37pm UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/2 "2024-07-23T22:37:11Z")

</div>

I am not familiar with the refs you mention but would like to chime in with a way of thinking that might benefit you. Model your data in a way that makes sense and the application will in a sense “build it self “. Is it possible for someone to have no friends? Then handle the empty list. If a user in _your_ app must have a friend (possibly because of an invite etc.) then that would be a “non empty list” (modeled by you or an existing package). Using the maybe type is not a sign of bad code but always using it as a replacement for JS undefined is. Does the missing value represent something? Does the missing value mean anything else then Result or your own type might be the way to go.

---

<div class="post-metadata">

**Author:** ![wolfadex](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/wolfadex/32/2461_2.png) [@wolfadex](https://discourse.elm-lang.org/u/wolfadex)\
**Post date:** [July 23, 2024, 11:25pm UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/3 "2024-07-23T23:25:35Z")

</div>

At work we use `Maybe.withDefault` a lot when mapping over a `Maybe a` trying to turn it into an HTML string. Something like

```elm
model.user
    |> Maybe.map .nickname
    |> Maybe.withDefault ""
    |> Html.text

```

because you either get a nickname or an empty node. This doesn’t work in every case but I do see it a fair amount.

* * *

For custom types instead of `Maybe a` I’m doing this with some hobby code right now (took a break to type this up). I have a function to search an `Array` and need to know if I found exactly the thing I’m looking for or approx the thing, or nothing at all. To handle this I have

```elm
type FindKeyedShape
    = KeyedShapeAlreadyAtTime
    | InterpolatedShape { shape : Shape, offset : Float, index : Int }
    | NoShapeFound

```

and can then handle each branch differently.

---

<div class="post-metadata">

**Author:** ![Sebastian](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/sebastian/32/98_2.png) [@Sebastian](https://discourse.elm-lang.org/u/Sebastian)\
**Post date:** [July 24, 2024, 1:22am UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/4 "2024-07-24T01:22:31Z")

</div>

Having a Maybe as the main data in a model is often appropiate. E.g. you didn’t find the user that was requested. It is ok to use `Maybe.map` a lot in the update function. You can create utility functions that encapsulate that boilerplate.

In your view, you might have a `case` with two views, one for the empty state and one for the present state. I usually leave `Maybe.withDefault` until the very end and only if it is really necessary.

---

<div class="post-metadata">

**Author:** ![ChrisWellsWood](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/chriswellswood/32/5293_2.png) [@ChrisWellsWood](https://discourse.elm-lang.org/u/ChrisWellsWood)\
**Post date:** [July 24, 2024, 12:16pm UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/5 "2024-07-24T12:16:28Z")

</div>

I don’t think that there are too many issues with having a bunch of Maybes in your model, but very often, if I have a group of them that are required to render a page (or Results/RemoteData etc), I’ll use a custom type for the model because it’s a bit more ergonomic once you have all the data. For example:

```elm
type Model
    = LoadingData { mToken : Maybe String, remoteData : RemoteData Http.Error MyPageData }
    | Page { token : String, pageData : MyPageData }
    | FailedToLoadData

view : Model -> Html Msg
view model =
    case model of
        LoadingData {mToken, remoteData } ->
            loadingView mToken remoteData
        Page token pageData ->
            pageView token pageData
        FailedToLoad ->
            failedToLoadView

```

This way, down in your `pageView`, you don’t need to check for maybes (or Results or RemoteData etc) everywhere.

---

<div class="post-metadata">

**Author:** ![dta](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dta/32/1917_2.png) [@dta](https://discourse.elm-lang.org/u/dta)\
**Post date:** [July 24, 2024, 12:59pm UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/6 "2024-07-24T12:59:47Z")

</div>

IMO, the advice against overusing Maybe is to help [make impossible states impossible](https://youtu.be/IcgmSRJHu_8). If it doesn’t make sense to be able to represent every possible combination of Just and Nothing, you should likely use a custom type.

---

<div class="post-metadata">

**Author:** ![badlydrawnrob](https://avatars.discourse-cdn.com/v4/letter/b/ee59a6/32.png) [@badlydrawnrob](https://discourse.elm-lang.org/u/badlydrawnrob)\
**Post date:** [July 25, 2024, 8:51pm UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/7 "2024-07-25T20:51:35Z")

</div>

So just to solidify and condense some of the suggestions on here, it seems to be:

1. Start with the data ([impossible states](https://www.youtube.com/watch?v=IcgmSRJHu_8) and [make data structures](https://www.youtube.com/watch?v=x1FU3e0sT1I))
2. Loading states might be best as custom `type`s
3. Potentially use non-empty lists …
4. ~~Or default data in your `json` (instead of a `Maybe`)?~~ See [below](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/11)
5. But `Maybe` types are fine to use, as is `Maybe.withDefault`.

## `Maybe.withDefault`

> [@wolfadex](#):
>
> ```auto
> model.user
> |> Maybe.map .nickname
> |> Maybe.withDefault ""
> |> Html.text
> 
> ```

@wolfadex I quite like that approach, do you normally just use it for a `string` or do you also use for other data types?

I understand the general idea or `FindKeyedShape`, but I can’t quite visualise how that search function might work.

## `Case`ing on a loading `type` …

@ChrisWellsWood I’ve seen this method before of `case`ing on your model  
for `noop`, `loading`, `loaded`, `failed` which makes sense … but could you expand a little on your custom type records? I understand `Page` (a title and it’s body), but …

- Why are you naming things `mToken` and `token`? What do they stand for?
- How do you pass your `loaded` data to update `Page` and `view` when it’s loaded? (I’m assuming `MyPageData` is the decoder?)

## Impossible states

@dta Do you personally have any other examples you could add to that line of thought? Simple ones ideally.

## What would a `Maybe` type look like in the `json`?

I’m [splitting this out into another question](https://discourse.elm-lang.org/t/how-do-you-store-maybe-and-custom-type-as-json/9902), as it brings up the question how to save and represent a `type` in the json file, be it a `Maybe` or a `type Custom`.

---

<div class="post-metadata">

**Author:** ![wolfadex](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/wolfadex/32/2461_2.png) [@wolfadex](https://discourse.elm-lang.org/u/wolfadex)\
**Post date:** [July 25, 2024, 9:59pm UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/8 "2024-07-25T21:59:29Z")

</div>

> [@badlydrawnrob](#):
>
> @wolfadex I quite like that approach, do you normally just use it for a `string` or do you also use for other data types?

We use it for numbers sometimes too, like

```elm
model.currencyValue
    |> Maybe.map Currency.format
    |> Maybe.withDefault "-" -- we typically use a dash for Nothing values in tables and lists
    |> Html.text

```

> [@badlydrawnrob](#):
>
> I understand the general idea or `FindKeyedShape`, but I can’t quite visualise how that search function might work.

I won’t go too deep but the gist is I have a `List { offset : Float, shape : Shape }` and I want to find say the `Shape` at an offset of 6000ms. I might have an actual `Shape` at that offset or I might be between 2 instances and want to average them. `KeyedShapeAlreadyAtTime` says I’m at an instance while `InterpolatedShape` gives my the interpolated value, and `NoShapeFound` is for other edge cases.

---

<div class="post-metadata">

**Author:** ![badlydrawnrob](https://avatars.discourse-cdn.com/v4/letter/b/ee59a6/32.png) [@badlydrawnrob](https://discourse.elm-lang.org/u/badlydrawnrob)\
**Post date:** [July 25, 2024, 10:10pm UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/9 "2024-07-25T22:10:18Z")

</div>

> [@wolfadex](#):
>
> I want to find say the `Shape` at an offset of 6000ms

You lost me after this 😂 it doesn’t matter, I get the general idea of using types to represent `found`, `nearlyFound` and `notFound`.

---

<div class="post-metadata">

**Author:** ![dta](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dta/32/1917_2.png) [@dta](https://discourse.elm-lang.org/u/dta)\
**Post date:** [July 25, 2024, 10:32pm UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/10 "2024-07-25T22:32:25Z")

</div>

One thing that came up recently was

```auto
debounce: Boolean
debounceInterval: Maybe Int

```

This was better as a custom type

```auto
type DebounceConfig = NoDebounce | Debounce Int

```

The original wasn’t exactly two maybes, but the principle is the same. I suppose we could have used one maybe for the solution (our custom type is equivalent to `Maybe Int`), but the custom type was clearer.

---

<div class="post-metadata">

**Author:** ![Sebastian](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/sebastian/32/98_2.png) [@Sebastian](https://discourse.elm-lang.org/u/Sebastian)\
**Post date:** [July 26, 2024, 1:14am UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/11 "2024-07-26T01:14:59Z")

</div>

> [@badlydrawnrob](#):
>
> - Or default data in your `json` (instead of a `Maybe`)?

I think you should prefer a Maybe, rather than default data. As default data just hides potential issues and mute errors. I think you should make it obvious that you are missing something, and push it until the very end to deal with that e.g. show an empty view.

> [@badlydrawnrob](#):
>
> - But `Maybe` types are fine to use, as is `Maybe.withDefault`.

Maybes are fine. Your own descriptive custom type is better.  
I recommend not reaching for `withDefault` early. Try to use `Maybe.withDefault` at the very end, e.g. in the view that needs to show something.

---

<div class="post-metadata">

**Author:** ![ChrisWellsWood](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/chriswellswood/32/5293_2.png) [@ChrisWellsWood](https://discourse.elm-lang.org/u/ChrisWellsWood)\
**Post date:** [July 26, 2024, 8:41am UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/12 "2024-07-26T08:41:53Z")

</div>

> @ChrisWellsWood I’ve seen this method before of `case`ing on your model  
> for `noop`, `loading`, `loaded`, `failed` which makes sense … but could you expand a little on your custom type records? I understand `Page` (a title and it’s body), but …
> 
> - Why are you naming things `mToken` and `token`? What do they stand for?
> - How do you pass your `loaded` data to update `Page` and `view` when it’s loaded? (I’m assuming `MyPageData` is the decoder?)

Sorry, I’ve distracted you with this stuff, they’re all just placeholder names that came off the top of my head. 🤣 `mToken` was meant to be something like a JWT that the user might need to request something from a server, `MyPageData` is just the model for your page with whatever stuff you need for it. I omitted the update function, that would change the `Model` type constructor based on whether the remote data had been received from the server.

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [August 5, 2024, 8:42am UTC](https://discourse.elm-lang.org/t/staying-sane-with-maybe-maybe-vs-type/9897/13 "2024-08-05T08:42:12Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
