- 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).
- The next two defs assign the names `birds` and `iguanas` to the expressions `3` and `2`.
- The last def assigns the name `total` to the expression `Num.toStr (birds + iguanas)`.
Once we have a def, we can use its name in other expressions.
For example, the `total` expression refers to `birds` and `iguanas`.
We can also refer to defs inside strings using *string interpolation*. The
string `"There are \(total) animals."` evaluates to the same thing as calling
`Str.concat "There are " (Str.concat total " animals.")` directly.
You can name a def using any combination of letters and numbers, but they have
to start with a letter. Note that definitions are constant; once we've assigned
a name to an expression, we can't reassign it! We'd get an error if we wrote this:
```coffee
birds = 3
birds = 2
```
Order of defs doesn't matter. We defined `birds` and `iguanas` before
`total` (which uses both of them), but we defined `main` before `total` even though
it uses `total`. If you like, you can change the order of these defs to anything
you like, and everything will still work the same way!
This works because Roc expressions don't have *side effects*. We'll talk more
about side effects later.
## Functions and `if`
So far we've called functions like `Num.toStr`, `Str.concat`, and `Stdout.line`.
Next let's try defining a function of our own.
```coffee
main = Stdout.line "There are \(total) animals."
birds = 3
iguanas = 2
total = addAndStringify birds iguanas
addAndStringify = \num1, num2 ->
Num.toStr (num1 + num2)
```
This new `addAndStringify` function we've defined takes two numbers, adds them,
calls `Num.toStr` on the result, and returns that. The `\num1, num2 ->` syntax
defines a function's arguments, and the expression after the `->` is the body
of the function. The expression at the end of the body (`Num.toStr (num1 + num2)`
in this case) is returned automatically.
Let's modify the function to return an empty string if the numbers add to zero.
```coffee
addAndStringify = \num1, num2 ->
sum = num1 + num2
if sum == 0 then
""
else
Num.toStr sum
```
We did a two things here:
* 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.
* We added an `if` / `then` / `else` conditional to return either `""` or `Num.toStr sum` depending on whether `sum == 0`
Of note, we couldn't have done `total = num1 + num2` because that would be
fromOriginal = { original & birds: 4, iguanas: 3 }
```
The `fromScratch` and `fromOriginal` records are equal, although they're assembled in
different ways.
*`fromScratch` was built using the same record syntax we've been using up to this point.
*`fromOriginal` created a new record using the contents of `original` as defaults for fields that it didn't specify after the `&`.
Note that when we do this, the fields you're overriding must all be present on the original record,
and their values must have the same type as the corresponding values in the original record.
## Tags
Sometimes we want to represent that something can have one of several values. For example:
```coffee
stoplightColor =
if something > 0 then
Red
else if something == 0 then
Yellow
else
Green
```
Here, `stoplightColor` can have one of three values: `Red`, `Yellow`, or `Green`.
The capitalization is very important! If these were lowercase (`red`, `yellow`, `green`),
then they would refer to defs. However, because they are capitalized, they instead
refer to *tags*.
A tag is a literal value just like a number or a string. Similarly to how I can write
the number `42` or the string `"forty-two"` without defining them first, I can also write
the tag `FortyTwo` without defining it first. Also, similarly to how `42 == 42` and
`"forty-two" == "forty-two"`, it's also the case that `FortyTwo == FortyTwo`.
Speaking of equals, if we put `42 == 42` into `roc repl`, the output we'll see is `True`.
This is because booleans in Roc are tags; a boolean is either the tag `True` or the tag
`False`. So I can write `if True then` or `if False then` and it will work as expected,
even though I'd get an error if I wrote `if "true" then` or `if 1 then`. (Roc doesn't
have a concept of "truthiness" - you always have to use booleans for conditionals!)
Let's say we wanted to turn `stoplightColor` from a `Red`, `Green`, or `Yellow` into
a string. Here's one way we could do that:
```elm
stoplightStr =
if stoplightColor == Red then
"red"
else if stoplightColor == Green then
"green"
else
"yellow"
```
We can express this logic more concisely using `when`/`is` instead of `if`/`then`:
```elm
stoplightStr =
when stoplightColor is
Red -> "red"
Green -> "green"
Yellow -> "yellow"
```
This results in in the same value for `stoplightStr`. In both the `when` version and the `if` version, we
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:
```coffee
stoplightStr =
when stoplightColor is
Red -> "red"
Green | Yellow ->
if contrast > 75 then
"not red, but very high contrast"
else if saturation > 50 then
"not red, but high contrast"
else
"not red"
```
Either style can be a reasonable choice depending on the cirumstances.
### Tags with payloads
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.
## Lists
Another thing we can do in Roc is to make a *list* of values. Here's an example:
```coffee
names = [ "Sam", "Lee", "Ari" ]
```
This is a list with three elements in it, all strings. We can add a fourth
The `*` is a *wildcard type* - that is, a type that's compatible with any other type. `List *` is compatible
with any type of `List` - so, `List Str`, `List Bool`, and so on. So you can call
`List.isEmpty [ "I am a List Str" ]` as well as `List.isEmpty [ True ]`, and they will both work fine.
The wildcard type also comes up with empty lists. Suppose we have one function that takes a `List Str` and another
function that takes a `List Bool`. We might reasonably expect to be able to pass an empty list (that is, `[]`) to
either of these functions. And so we can! This is because a `[]` value has the type `List *` - that is,
"a list with a wildcard type parameter," or "a list whose element type could be anything."
`List.reverse` works similarly to `List.isEmpty`, but with an important distinction. As with `isEmpty`, we can
call `List.reverse` on any list, regardless of its type parameter. However, consider these calls:
```coffee
strings : List Str
strings = List.reverse [ "a", "b" ]
bools : List Bool
bools = List.reverse [ True, False ]
```
In the `strings` example, we have `List.reverse` returning a `List Str`. In the `bools` example, it's returning a
`List Bool`. So what's the type of `List.reverse`?
We saw that `List.isEmpty` has the type `List * -> Bool`, so we might think the type of `List.reverse` would be
`reverse : List * -> List *`. However, remember that we also saw that the type of the empty list is `List *`?
`List * -> List *` is actually the type of a function that always returns empty lists! That's not what we want.
What we want is something like one of these:
```coffee
reverse : List elem -> List elem
```
```coffee
reverse : List value -> List value
```
```coffee
reverse : List a -> List a
```
Any of these will work, because `elem`, `value`, and `a` are all *type variables*. A type variable connects
two or more types in the same annotation. So you can read `List elem -> List elem` as "takes a list and returns
a list that has the same element type." Just like `List.reverse` does!
You can choose any name you like for a type variable, but it has to be lowercase. (You may heve noticed all the
types we've used until now are uppercase; that is no accident! Lowercase types are always type variables, so
all other named types have to be uppercase.) All three of the above type annotations are equivalent;
the only difference is that we chose different names (`elem`, `value`, and `a`) for their type variables.
You can tell some interesting things about functions based on the type parameters involved. For example,
any function that returns `List *` definitely always returns an empty list. You don't need to look at the rest
of the type annotation, or even the function's implementation! The only way to have a function that returns
`List *` is if it returns an empty list.
Similarly, the only way to have a function whose type is `a -> a` is if the function's implementation returns
its argument without modifying it in any way. This is known as [the identity function](https://en.wikipedia.org/wiki/Identity_function).
### Numeric types
[ This part of the tutorial has not been written yet. Coming soon! ]
### Open and closed records
[ This part of the tutorial has not been written yet. Coming soon! ]
### Open and closed tag unions
[ This part of the tutorial has not been written yet. Coming soon! ]
## Interface modules
[ 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`:
```coffee
app "hello"
packages [ pf: "examples/cli/platform" ]
imports [ pf.Stdout ]
provides main to pf
```
This is known as a *module header*. Every `.roc` file is a *module*, and there
are different types of modules. We know this particular one is an *application module*
(or *app module* for short) because it begins with the `app` keyword.
The line `app "hello"` states that this module defines a Roc application, and
that building this application should produce an executable named `hello`. This
means when you run `roc Hello.roc`, the Roc compiler will build an executable
named `hello` (or `hello.exe` on Windows) and run it. You can also build the executable
without running it by running `roc build Hello.roc`.
The remaining lines all involve the *platform* this application is built on:
```coffee
packages [ pf: "examples/cli/platform" ]
imports [ pf.Stdout ]
provides main to pf
```
The `packages [ pf: "examples/cli/platform" ]` part says two things:
- We're going to be using a *package* (that is, a collection of modules) called `"examples/cli/platform"`
- We're going to name that package `pf` so we can refer to it more concisely in the future.
The `imports [ pf.Stdout ]` line says that we want to import the `Stdout` module
from the `pf` package, and make it available in the current module.
This import has a direct interaction with our definition of `main`. Let's look
at that again:
```coffee
main = Stdout.line "I'm a Roc application!"
```
Here, `main` is calling a function called `Stdout.line`. More specifically, it's
calling a function named `line` which is exposed by a module named
`Stdout`.
When we write `imports [ pf.Stdout ]`, it specifies that the `Stdout`
module comes from the `pf` package.
Since `pf` was the name we chose for the `examples/cli/platform` package
(when we wrote `packages [ pf: "examples/cli/platform" ]`), this `imports` line
tells the Roc compiler that when we call `Stdout.line`, it should look for that
`line` function in the `Stdout` module of the `examples/cli/platform` package.
# Building a Command-Line Interface (CLI)
## Tasks
Tasks are technically not part of the Roc language, but they're very common in
platforms. Let's use the CLI platform in `examples/cli` as an example!
In the Tutorial platform, we have four operations we can do:
* Write a string to the console
* Read a string from user input
* Write a string to a file
* Read a string from a file
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!
## Operator Desugaring Table
Here are various Roc expressions involving operators, and what they desugar to.