Desugaring ?

What's syntax sugar?

Syntax within a programming language that is designed to make things easier to read or express. It allows developers to write code in a more concise, readable, or convenient way without adding new functionality to the language itself.

Desugaring converts syntax sugar (like x + 1) into more fundamental operations (like Num.add(x, 1)).

Let's see how ? is desugared. In this example we will extract the name and birth year from a string like "Alice was born in 1990".


parse_name_and_year : Str -> Result { name : Str, birth_year : U16 } _
parse_name_and_year = |str|
    { before: name, after: birth_year_str } = Str.split_first(str, " was born in ")?
    birth_year = Str.to_u16(birth_year_str)?
    Ok({ name, birth_year })

After desugaring, this becomes:


parse_name_and_year_try = |str|
    when Str.split_first(str, " was born in ") is
        Err(err1) ->
            return Err(err1)

        Ok({ before: name, after: birth_year_str }) ->
            when Str.to_u16(birth_year_str) is
                Err(err2) ->
                    return Err(err2)

                Ok(birth_year) ->
                    Ok({ name, birth_year })

So birth_year = Str.to_u16(birth_year_str)? is converted to

when Str.to_u16(birth_year_str) is
    Err(err2) -> return Err(err2)
    Ok(birth_year) -> birth_year

As you can see, the first version is a lot nicer!

Thanks to ?, you can write code in a familiar way and you get the benefits of Roc's error handling to drastically reduce the likelihood of crashes.

Full Code

app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br" }

import cli.Stdout

main! = |_args|
    Stdout.line!(Inspect.to_str(parse_name_and_year("Alice was born in 1990")))?
    Stdout.line!(Inspect.to_str(parse_name_and_year_try("Alice was born in 1990")))?

    Ok({})

### start snippet question
parse_name_and_year : Str -> Result { name : Str, birth_year : U16 } _
parse_name_and_year = |str|
    { before: name, after: birth_year_str } = Str.split_first(str, " was born in ")?
    birth_year = Str.to_u16(birth_year_str)?
    Ok({ name, birth_year })
### end snippet question

### start snippet desugared
parse_name_and_year_try = |str|
    when Str.split_first(str, " was born in ") is
        Err(err1) ->
            return Err(err1)

        Ok({ before: name, after: birth_year_str }) ->
            when Str.to_u16(birth_year_str) is
                Err(err2) ->
                    return Err(err2)

                Ok(birth_year) ->
                    Ok({ name, birth_year })
### end snippet desugared

expect parse_name_and_year("Alice was born in 1990") == Ok({ name: "Alice", birth_year: 1990 })
expect parse_name_and_year_try("Alice was born in 1990") == Ok({ name: "Alice", birth_year: 1990 })

Output

Run this from the directory that has main.roc in it:

$ roc main.roc
Ok({birth_year: 1990, name: "Alice"})
Ok({birth_year: 1990, name: "Alice"})