- The first def assigns the name `main` to the expression `Stdout.line "I have \(numDefs) definitions."`. The `Stdout.line` function takes a string and prints it as a line to [`stdout`] (the terminal's standard output device).
* We introduced a local def named `sum`, and set it equal to `num1 + num2`. Because we defined `sum` inside `addAndStringify`, it will not be accessible outside that function.
have three conditional branches, and each of them evaluates to a string. The difference is how the
conditions are specified; here, we specify between `when` and `is` that we're making comparisons against
`stoplightColor`, and then we specify the different things we're comparing it to: `Red`, `Green`, and `Yellow`.
Besides being more concise, there are other advantages to using `when` here.
1. We don't have to specify an `else` branch, so the code can be more self-documenting about exactly what all the options are.
2. We get more compiler help. If we try deleting any of these branches, we'll get a compile-time error saying that we forgot to cover a case that could come up. For example, if we delete the `Green ->` branch, the compiler will say that we didn't handle the possibility that `stoplightColor` could be `Green`. It knows this because `Green` is one of the possibilities in our `stoplightColor = if …` definition.
We can still have the equivalent of an `else` branch in our `when` if we like. Instead of writing "else", we write
"_ ->" like so:
```coffee
stoplightStr =
when stoplightColor is
Red -> "red"
_ -> "not red"
```
This lets us more concisely handle multiple cases. However, it has the downside that if we add a new case -
for example, if we introduce the possibility of `stoplightColor` being `Orange`, the compiler can no longer
tell us we forgot to handle that possibility in our `when`. After all, we are handling it - just maybe not
in the way we'd decide to if the compiler had drawn our attention to it!
We can make this `when`*exhaustive* (that is, covering all possibilities) without using `_ ->` by using
`|` to specify multiple matching conditions for the same branch:
```coffee
stoplightStr =
when stoplightColor is
Red -> "red"
Green | Yellow -> "not red"
```
You can read `Green | Yellow` as "either `Green` or `Yellow`". By writing it this way, if we introduce the
possibility that `stoplightColor` can be `Orange`, we'll get a compiler error telling us we forgot to cover
that case in this `when`, and then we can handle it however we think is best.
We can also combine `if` and `when` to make branches more specific:
```coffee
stoplightStr =
when stoplightColor is
Red -> "red"
Green | Yellow if contrast > 75 -> "not red, but very high contrast"
Green | Yellow if contrast > 50 -> "not red, but high contrast"
Green | Yellow -> "not red"
```
This will give the same answer for `spotlightStr` as if we had written the following:
Tags can have *payloads* - that is, values contained within them. For example:
```coffee
stoplightColor =
if something > 100 then
Red
else if something > 0 then
Yellow
else if something == 0 then
Green
else
Custom "some other color"
stoplightStr =
when stoplightColor is
Red -> "red"
Green | Yellow -> "not red"
Custom description -> description
```
This makes two changes to our earlier `stoplightColor` / `stoplightStr` example.
1. We sometimes set `stoplightColor` to be `Custom "some other color"`. When we did this, we gave the `Custom` tag a *payload* of the string `"some other color"`.
2. We added a `Custom` tag in our `when`, with a payload which we named `description`. Because we did this, we were able to refer to `description` in the body of the branch (that is, the part after the `->`) just like any other def.
Any tag can be given a payload like this. A payload doesn't have to be a string; we could also have said (for example) `Custom { r: 40, g: 60, b: 80 }` to specify an RGB color instead of a string. Then in our `when` we could have written `Custom record ->` and then after the `->` used `record.r`, `record.g`, and `record.b` to access the `40`, `60`, `80` values. We could also have written `Custom { r, g, b } ->` to *destructure* the record, and then
accessed these `r`, `g`, and `b` defs after the `->` instead.
We refer to whatever comes before a `->` in a `when` expression as a *pattern* - so for example, in the
`Custom description -> description` branch, `Custom description` would be a pattern. In programming, using
patterns in branching conditionals like `when` is known as [pattern matching](https://en.wikipedia.org/wiki/Pattern_matching). You may hear people say things like "let's pattern match on `Custom` here" as a way to
suggest making a `when` branch that begins with something like `Custom description ->`.
Roc has different numeric types that each have different tradeoffs.
They can all be broken down into two categories: [fractions](https://en.wikipedia.org/wiki/Fraction),
and [integers](https://en.wikipedia.org/wiki/Integer). In Roc we call these `Frac` and `Int` for short.
### Integers
Roc's integer types have two important characteristics: their *size* and their [*signedness*](https://en.wikipedia.org/wiki/Signedness).
Together, these two characteristics determine the range of numbers the integer type can represent.
For example, the Roc type `U8` can represent the numbers 0 through 255, whereas the `I16` type can represent
the numbers -32768 through 32767. You can actually infer these ranges from their names (`U8` and `I16`) alone!
The `U` in `U8` indicates that it's *unsigned*, meaning that it can't have a minus [sign](https://en.wikipedia.org/wiki/Sign_(mathematics)), and therefore can't be negative. The fact that it's unsigned tells us immediately that
its lowest value is zero. The 8 in `U8` means it is 8 [bits](https://en.wikipedia.org/wiki/Bit) in size, which
means it has room to represent 2⁸ (which is equal to 256) different numbers. Since one of those 256 different numbers
is 0, we can look at `U8` and know that it goes from `0` (since it's unsigned) to `255` (2⁸ - 1, since it's 8 bits).
If we change `U8` to `I8`, making it a *signed* 8-bit integer, the range changes. Because it's still 8 bits, it still
has room to represent 2⁸ (that is, 256) different numbers. However, now in addition to one of those 256 numbers
Choosing a size depends on your performance needs and the range of numbers you want to represent. Consider:
* Larger integer sizes can represent a wider range of numbers. If you absolutely need to represent numbers in a certain range, make sure to pick an integer size that can hold them!
* Smaller integer sizes take up less memory. These savings rarely matters in variables and function arguments, but the sizes of integers that you use in data structures can add up. This can also affect whether those data structures fit in [cache lines](https://en.wikipedia.org/wiki/CPU_cache#Cache_performance), which can easily be a performance bottleneck.
* Certain processors work faster on some numeric sizes than others. There isn't even a general rule like "larger numeric sizes run slower" (or the reverse, for that matter) that applies to all processors. In fact, if the CPU is taking too long to run numeric calculations, you may find a performance improvement by experimenting with numeric sizes that are larger than otherwise necessary. However, in practice, doing this typically degrades overall performance, so be careful to measure properly!
Here are the different fixed-size integer types that Roc supports:
While the fixed-point `Dec` has a fixed range, the floating-point `F32` and `F64` do not.
Instead, outside of a certain range they start to lose precision instead of immediately overflowing
the way integers and `Dec` do. `F64` can represent [between 15 and 17 significant digits](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) before losing precision, whereas `F32` can only represent [between 6 and 9](https://en.wikipedia.org/wiki/Single-precision_floating-point_format#IEEE_754_single-precision_binary_floating-point_format:_binary32).
There are some use cases where `F64` and `F32` can be better choices than `Dec`
despite their precision drawbacks. For example, in graphical applications they
can be a better choice for representing coordinates because they take up less memory,
various relevant calculations run faster, and decimal precision loss isn't as big a concern
when dealing with screen coordinates as it is when dealing with something like currency.
### Num, Int, and Frac
Some operations work on specific numeric types - such as `I64` or `Dec` - but operations support
multiple numeric types. For example, the `Num.abs` function works on any number, since you can
take the [absolute value](https://en.wikipedia.org/wiki/Absolute_value) of integers and fractions alike.
[ This part of the tutorial has not been written yet. Coming soon! ]
## Builtin modules
There are several modules that are built into the Roc compiler, which are imported automatically into every
Roc module. They are:
1.`Bool`
2.`Str`
3.`Num`
4.`List`
5.`Result`
6.`Dict`
7.`Set`
You may have noticed that we already used the first five - for example, when we wrote `Str.concat` and `Num.isEven`,
we were referencing functions stored in the `Str` and `Num` modules.
These modules are not ordinary `.roc` files that live on your filesystem. Rather, they are built directly into the
Roc compiler. That's why they're called "builtins!"
Besides being built into the compiler, the builtin modules are different from other modules in that:
* They are always imported. You never need to add them to `imports`.
* All their types are imported unqualified automatically. So you never need to write `Num.Nat`, because it's as if the `Num` module was imported using `imports [ Num.{ Nat } ]` (and the same for all the other types in the `Num` module).
## The app module header
Let's take a closer look at the part of `Hello.roc` above `main`:
We'll use these four operations to learn about tasks.
First, let's do a basic "Hello World" using the tutorial app.
```coffee
app "cli-tutorial"
packages { pf: "examples/cli/platform" }
imports [ pf.Stdout ]
provides [ main ] to pf
main =
Stdout.line "Hello, World!"
```
The `Stdout.line` function takes a `Str` and writes it to [standard output](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)).
It has this type:
```coffee
Stdout.line : Str -> Task {} *
```
A `Task` represents an *effect* - that is, an interaction with state outside your Roc program,
such as the console's standard output, or a file.
When we set `main` to be a `Task`, the task will get run when we run our program. Here, we've set
`main` to be a task that writes `"Hello, World!"` to `stdout` when it gets run, so that's what
our program does!
`Task` has two type parameters: the type of value it produces when it finishes running, and any
errors that might happen when running it. `Stdout.line` has the type `Task {} *` because it doesn't
produce any values when it finishes (hence the `{}`) and there aren't any errors that can happen
when it runs (hence the `*`).
In contrast, `Stdin.line` produces a `Str` when it finishes reading from [standard input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)). That `Str` is reflected in its type:
```coffee
Stdin.line : Task Str *
```
Let's change `main` to read a line from `stdin`, and then print it back out again:
```swift
app "cli-tutorial"
packages { pf: "examples/cli/platform" }
imports [ pf.Stdout, pf.Stdin, pf.Task ]
provides [ main ] to pf
main =
Task.await Stdin.line \text ->
Stdout.line "You just entered: \(text)"
```
If you run this program, at first it won't do anything. It's waiting for you to type something
in and press Enter! Once you do, it should print back out what you entered.
The `Task.await` function combines two tasks into one bigger `Task` which first runs one of the
given tasks and then the other. In this case, it's combining a `Stdin.line` task with a `Stdout.line`
task into one bigger `Task`, and then setting `main` to be that bigger task.
The type of `Task.await` is:
```haskell
Task.await : Task a err, (a -> Task b err) -> Task b err
```
The second argument to `Task.await` is a "callback function" which runs after the first task
completes. This callback function receives the output of that first task, and then returns
the second task. This means the second task can make use of output from the first task, like
we did in our `\text -> …` callback function here:
```swift
\text ->
Stdout.line "You just entered: \(text)"
```
Notice that, just like before, we're still setting `main` to be a single `Task`. This is how we'll
always do it! We'll keep building up bigger and bigger `Task`s out of smaller tasks, and then setting
`main` to be that one big `Task`.
For example, we can print a prompt before we pause to read from `stdin`, so it no longer looks like
the program isn't doing anything when we start it up:
Here we've changed how we're importing the `Task` module. Before it was
`pf.Task` and now it's `pf.Task.{ await }`. The difference is that we're
importing `await` in an *unqualified* way, meaning now whenever we write `await`
in this module, it will refer to `Task.await` - so we no longer need to write
`Task.` every time we want to `await`.
It's most common in Roc to call functions from other modules in a *qualified* way
(`Task.await`) rather than unqualified (`await`) like this, but it can be nice
for a function with an uncommon name (like "await") which often gets called repeatedly
across a small number of lines of code.
Speaking of calling `await` repeatedly, if we keep calling it more and more on this
code, we'll end up doing a lot of indenting. If we'd rather not indent so much, we
can rewrite `main` into this style which looks different but does the same thing:
```swift
main =
_ <-await(Stdout.line"TypesomethingpressEnter:")
text <-awaitStdin.line
Stdout.line "You just entered: \(text)"
```
This `<-` syntax is called *backpassing*. The `<-` is a way to define an
anonymous function, just like `\ … ->` is.
Here, we're using backpassing to define two anonymous functions. Here's one of them:
```swift
text <-
Stdout.line "You just entered: \(text)"
```
It may not look like it, but this code is defining an anonymous function! You might
remember it as the anonymous function we previously defined like this:
```swift
\text ->
Stdout.line "You just entered: \(text)"
```
These two anonymous functions are the same, just defined using different syntax.
The reason the `<-` syntax is called *backpassing* is because it both defines a
function and passes that function *back* as an argument to whatever comes after
the `<-` (which in this case is `await Stdin.line`).
Let's look at these two complete expressions side by side. They are both
saying exactly the same thing, with different syntax!
Here's the original:
```swift
await Stdin.line \text ->
Stdout.line "You just entered: \(text)"
```
And here's the equivalent expression with backpassing syntax:
```swift
text <-awaitStdin.line
Stdout.line "You just entered: \(text)"
```
Here's the other function we're defining with backpassing:
```swift
_ <-
text <-awaitStdin.line
Stdout.line "You just entered: \(text)"
```
We could also have written that function this way if we preferred:
```swift
_ <-
await Stdin.line \text ->
Stdout.line "You just entered: \(text)"
```
This is using a mix of a backpassing function `_ <-` and a normal function `\text ->`,
which is totally allowed! Since backpassing is nothing more than syntax sugar for
defining a function and passing back as an argument to another function, there's no
reason we can't mix and match if we like.
That said, the typical style in which this `main` would be written in Roc is using
backpassing for all the `await` calls, like we had above:
```swift
main =
_ <-await(Stdout.line"TypesomethingpressEnter:")
text <-awaitStdin.line
Stdout.line "You just entered: \(text)"
```
This way, it reads like a series of instructions:
1. First, run the `Stdout.line` task and await its completion. Ignore its output (hence the underscore in `_ <-`)
2. Next, run the `Stdin.line` task and await its completion. Name its output `text`.
3. Finally, run the `Stdout.line` task again, using the `text` value we got from the `Stdin.line` effect.
Some important things to note about backpassing and `await`:
*`await` is not a language keyword in Roc! It's referring to the `Task.await` function, which we imported unqualified by writing `Task.{ await }` in our module imports. (That said, it is playing a similar role here to the `await` keyword in languages that have `async`/`await` keywords, even though in this case it's a function instead of a special keyword.)
* Backpassing syntax does not need to be used with `await` in particular. It can be used with any function.
* Roc's compiler treats functions defined with backpassing exactly the same way as functions defined the other way. The only difference between `\text ->` and `text <-` is how they look, so feel free to use whichever looks nicer to you!
That last one works because a function accepting an open union can accept any unrecognized tag, including
`Ok Str` - even though it is not mentioned as one of the tags in `[ Err Bool, Whatever ]*`! Remember, when
a function accepts an open tag union, any `when` branches on that union must include a catch-all `_ ->` branch,
which is the branch that will end up handling the `Ok Str` value we pass in.
However, I could not pass an `[ Ok Str ]*` to a function with a *closed* tag union argument that did not
mention `Ok Str` as one of its tags. So if I tried to pass `[ Ok Str ]*` to a function with the type
`[ Err Bool, Whatever ] -> Str`, I would get a type mismatch - because a `when` in that function could
be handling the `Err Bool` possibility and the `Whatever` possibility, and since it would not necessarily have
a catch-all `_ ->` branch, it might not know what to do with an `Ok Str` if it received one.
> **Note:** It wouldn't be accurate to say that a function which accepts an open union handles
> "all possible tags." For example, if I have a function `[ Ok Str ]* -> Bool` and I pass it
> `Ok 5`, that will still be a type mismatch. If you think about it, a `when` in that function might
> have the branch `Ok str ->` which assumes there's a string inside that `Ok`, and if `Ok 5` type-checked,
> then that assumption would be false and things would break!
>
> So `[ Ok Str ]*` is more restrictive than `[]*`. It's basically saying "this may or may not be an `Ok` tag,
> but if it is an `Ok` tag, then it's guaranteed to have a payload of exactly `Str`."
In summary, here's a way to think about the difference between open unions in a value you have, compared to a value you're accepting:
* If you *have* a closed union, that means it has all the tags it ever will, and can't accumulate more.
* If you *have* an open union, that means it can accumulate more tags through conditional branches.
* If you *accept* a closed union, that means you only have to handle the possibilities listed in the union.
* If you *accept* an open union, that means you have to handle the possibility that it has a tag you can't know about.
## Type Variables in Tag Unions
Earlier we saw these two examples, one with an open tag union and the other with a closed one:
```coffee
example : [ Foo Str, Bar Bool ]* -> Bool
example = \tag ->
when tag is
Foo str -> Str.isEmpty str
Bar bool -> bool
_ -> False
```
```coffee
example : [ Foo Str, Bar Bool ] -> Bool
example = \tag ->
when tag is
Foo str -> Str.isEmpty str
Bar bool -> bool
```
Similarly to how there are open records with a `*`, closed records with nothing,
and constrained records with a named type variable, we can also have *constrained tag unions*
with a named type variable. Here's an example:
```coffee
example : [ Foo Str, Bar Bool ]a -> [ Foo Str, Bar Bool ]a
example = \tag ->
when tag is
Foo str -> Bar (Str.isEmpty str)
Bar _ -> Bar False
other -> other
```
This type says that the `example` function will take either a `Foo Str` tag, or a `Bar Bool` tag,
or possibly another tag we don't know about at compile time - and it also says that the function's
return type is the same as the type of its argument.
So if we give this function a `[ Foo Str, Bar Bool, Baz (List Str) ]` argument, then it will be guaranteed
to return a `[ Foo Str, Bar Bool, Baz (List Str) ]` value. This is more constrained than a function that
returned `[ Foo Str, Bar Bool ]*` because that would say it could return *any* other tag (in addition to
the `Foo Str` and `Bar Bool` we already know about).
If we removed the type annotation from `example` above, Roc's compiler would infer the same type anyway.
This may be surprising if you look closely at the body of the function, because:
* The return type includes `Foo Str`, but no branch explicitly returns `Foo`. Couldn't the return type be `[ Bar Bool ]a` instead?
* The argument type includes `Bar Bool` even though we never look at `Bar`'s payload. Couldn't the argument type be inferred to be `Bar *` instead of `Bar Bool`, since we never look at it?
The reason it has this type is the `other -> other` branch. Take a look at that branch, and ask this question:
"What is the type of `other`?" There has to be exactly one answer! It can't be the case that `other` has one
type before the `->` and another type after it; whenever you see a named value in Roc, it is guaranteed to have
the same type everywhere it appears in that scope.
For this reason, any time you see a function that only runs a `when` on its only argument, and that `when`