compiler icon indicating copy to clipboard operation
compiler copied to clipboard

Unwrap Maybe in if statements

Open torepettersen opened this issue 5 years ago • 5 comments

In Rust there is a type called Option similar to Elm's Maybe.

Rust let you unwrap the Option in a if statment like this:

if let Some(a) = Some(1) {
    // Do something with a
}

Would it be possible to do something similar in Elm? Maybe like this? Or maybe another way that works better for Elm?

if let Just a = Just 1 then
    -- Do something with a

Now it seems like the only option I have is to use case of like this.

case Just 1 of
    Just a ->
        -- Do something with a
    Nothing ->
       -- Return something that represents nothing

This would e.g come in handy for error handling

div [] [
    if let Just error = hasError form FieldName then
        p [ class "has-error" ] [ text error ]
    -- else Return something that represents nothing
]

torepettersen avatar Feb 20 '20 19:02 torepettersen

Thanks for reporting this! To set expectations:

  • Issues are reviewed in batches, so it can take some time to get a response.
  • Ask questions a community forum. You will get an answer quicker that way!
  • If you experience something similar, open a new issue. We like duplicates.

Finally, please be patient with the core team. They are trying their best with limited resources.

github-actions[bot] avatar Feb 20 '20 19:02 github-actions[bot]

Interesting - I think it highlights a difference with rust and elm, perhaps. Your if let example is a rust expression that results in a () type - it lacks an else clause, so () is implied if the let fails. Therefore the other clause must return () as well. As a whole the expression returns no information, therefore its only useful as a side effect, like this:

    if let Some(i) = number {
        println!("Matched {:?}!", i);
    }

In elm side effects are not allowed, so there's no reason to have an expression like this.

If you have an else clause, then if let can return something besides ():

    if let Some(i) = number {
        true
    }
    else
    {
       false
    }

This example would return a boolean because the implied clause is there if the let fails. But at that point, why not just use a case?

bburdette avatar Feb 20 '20 20:02 bburdette

I highly recommend this talk about working with Maybes:

https://youtu.be/43eM4kNbb6c

You could use Maybe.andThen, Maybe.map and Maybe.withDefault

eimfach avatar Feb 20 '20 20:02 eimfach

It seems like it might be impossible to handle this in the same manner in a functional language like Elm.

Elm functions always have to return a type that you can use. While Rust returns an empty type () (which you cannot use for anything) unless you specify otherwise. Rust lets you compile as long as you don't explicitly set a return value (returning ()).

if let Some(a) = Some(1) {
   println!("{}", a);
};

But will not let you comple if you have a return value other than ().

if let Some(a) = Some(1) {
   a
};
// ^ expected `()`, found integer

Unless you handle the other case with the else statement.

if let Some(a) = Some(1) {
    a
} else {
    0
};

However, I found a quite simple solution to my problem that I am satisfied with.

I created myself a helper function with an empty default value.

div [] [
    Just "error message"
        |> justOrNothing (\error -> p [ class "has-error" ] [ text error ])
]

justOrNothing : (a -> Html msg) -> Maybe a -> Html msg
justOrNothing callback maybe =
    case maybe of
        Just a -> callback a
        Nothing -> text ""

torepettersen avatar Feb 20 '20 22:02 torepettersen

I created myself a helper function with an empty default value.

For others finding this issue in the future, check out the elm-community/html-extra package for some nice helpers for conditionals and Maybes in view code, including one that is equivalent to justOrNothing above, Html.Extra.viewMaybe.

adamdicarlo avatar Sep 01 '22 22:09 adamdicarlo