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, we'll start with a simple example:


helloBang =
    Stdout.line! "Hello Alice"
    Stdout.line "Hello Bob"

After desugaring, this becomes:


    Task.await (Stdout.line "Hello Alice") \_ ->
        Stdout.line "Hello Bob"

Task.await takes the success value from a given Task and uses that to generate a new Task. It's type is Task a b, (a -> Task c b) -> Task c b.

The type of Stdout.line is Str -> Task {} [StdoutErr Err]. Because Stdout.line does not return anything upon success, we use \_ in the desugared version, there is nothing to pass to the next Task.

You'll see that the version with ! looks a lot simpler!

Note that for the last line in the first snippet Stdout.line "Hello Bob", you could have also written Stdout.line! "Hello Bob". ! is not necessary on the last line but we allow it for consistency and to prevent confusion for beginners.

! also makes it easy to work with variables, let's take a look:


readInputBang =
    Stdout.line! "Type in something and press Enter:"
    input = Stdin.line!
    Stdout.line! "Your input was: $(input)"

This gets desugared to:


readInputAwait =
    Task.await (Stdout.line "Type in something and press Enter:") \_ ->
        Task.await Stdin.line \input ->
            Stdout.line "Your input was: $(input)"

This is similar to before but now the input = Stdin.line! gets converted to Task.await Stdin.line \input ->. With ! you can write code in a mostly familiar way while also getting the benefits of Roc's error handling and the seperation of pure and effectful code.

Note: this desugaring is very similar to that of ?.

Full Code

app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }

import pf.Stdin
import pf.Stdout

main =
    helloBang!
    helloAwait!
    readInputBang!
    readInputAwait!

### start snippet bang
helloBang =
    Stdout.line! "Hello Alice"
    Stdout.line "Hello Bob"
### end snippet bang

helloAwait =
    ### start snippet await
    Task.await (Stdout.line "Hello Alice") \_ ->
        Stdout.line "Hello Bob"
### end snippet await

### start snippet bangInput
readInputBang =
    Stdout.line! "Type in something and press Enter:"
    input = Stdin.line!
    Stdout.line! "Your input was: $(input)"
### end snippet bangInput

### start snippet awaitInput
readInputAwait =
    Task.await (Stdout.line "Type in something and press Enter:") \_ ->
        Task.await Stdin.line \input ->
            Stdout.line "Your input was: $(input)"
### end snippet awaitInput