1<input id="tutorial-toc-toggle" name="tutorial-toc-toggle" type= "checkbox">
2<nav id="tutorial-toc" aria-label="Table of Contents">
3 <label id="close-tutorial-toc" for="tutorial-toc-toggle">close</label>
4 <!-- TODO fix search: input [id "toc-search", type "text", placeholder "Search"] [] -->
5 <ol>
6 <li><a href="#repl">REPL</a></li>
7 <li><a href="#building-an-application">Building an Application</a></li>
8 <li><a href="#pattern-matching">Pattern Matching</a></li>
9 <li><a href="#types">Types</a></li>
10 <li><a href="#crashing">Crashing</a></li>
11 <li><a href="#testing">Testing</a></li>
12 <li><a href="#modules">Modules</a></li>
13 <li><a href="#advanced-concepts">Advanced Concepts</a></li>
14 </ol>
15</nav>
16<section id="tutorial-body">
17 <section>
18 <h1>Tutorial<label id="tutorial-toc-toggle-label" for="tutorial-toc-toggle">contents</label></h1>
19 <p>Welcome to Roc!</p>
20 <p>This tutorial will teach you how to build Roc applications. Along the way, you'll learn how to write tests, use the REPL, and more!</p>
21 </section>
22 <section>
23 <h2 id="installation"><a href="#installation">Installation</a></h2>
24 <p>Roc doesnβt have a numbered release or an installer yet, but you can follow the install instructions for your OS<a href="/install/getting_started.html#installation"> here </a>. If you get stuck, friendly people will be happy to help if you open a topic in<a href="https://roc.zulipchat.com/#narrow/stream/231634-beginners"> #beginners </a>on<a href="https://roc.zulipchat.com/"> Roc Zulip Chat </a>and ask for assistance!</p>
25 </section>
26
27## [AI Docs](#ai-docs) {#ai-docs}
28
29We have experimental AI-friendly text files for our [tutorial](/llms.txt) and [standard library](/builtins/llms.txt) that you can use to prompt your favorite large language model to answer your questions about Roc!
30
31## [REPL](#repl) {#repl}
32
33Let's start by getting acquainted with Roc's [_Read-Eval-Print-Loop_](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop), or **REPL** for short.
34
35You can use the online REPL at [roc-lang.org/repl](https://www.roc-lang.org/repl).
36
37Or you can run this in a terminal: <code class="block">roc repl</code>, and if Roc is [installed](/install), you should see this:
38
39<pre>
40<samp>
41 The rockinβ roc repl
42ββββββββββββββββββββββββ
43
44Enter an expression, or :help, or :q to quit.
45
46</samp></pre>
47
48So far, so good!
49
50### [Hello, World!](#hello-world) {#hello-world}
51
52Try typing this in the REPL and pressing Enter:
53
54<pre><samp class="repl-prompt">"Hello, World!"</samp></pre>
55
56The REPL should cheerfully display the following:
57
58<pre><samp><span class="literal">"Hello, World!" </span><span class="colon">:</span> Str</samp></pre>
59
60Congratulations! You've just written your first Roc code.
61
62### [Naming Things](#naming-things) {#naming-things}
63
64When you entered the _expression_ `"Hello, World!"`, the REPL printed it back out. It also printed `: Str`, because `Str` is that expression's type. We'll talk about types later; for now, let's ignore the `:` and whatever comes after it whenever we see them.
65
66You can assign specific names to expressions. Try entering these lines:
67
68```
69greeting = "Hi"
70audience = "World"
71```
72
73From now until you exit the REPL, you can refer to either `greeting` or `audience` by those names! We'll use these later on in the tutorial.
74
75### [Arithmetic](#arithmetic) {#arithmetic}
76
77Now let's try using an _operator_, specifically the `+` operator. Enter this:
78
79<pre><samp class="repl-prompt">1 <span class="op">+</span> 1</samp></pre>
80
81You should see this output:
82
83<pre><samp>2 <span class="colon">:</span> Num * <span class="comment"></span></samp></pre>
84
85According to the REPL, one plus one equals two. Sounds right!
86
87Roc will respect [order of operations](https://en.wikipedia.org/wiki/Order_of_operations) when using multiple arithmetic operators like `+` and `-`, but you can use parentheses to specify exactly how they should be grouped.
88
89<pre><samp><span class="repl-prompt">1 <span class="op">+</span> 2 <span class="op">*</span> (3 <span class="op">-</span> 4)
90
91-1 <span class="colon">:</span> Num *
92</span></samp></pre>
93
94### [Calling Functions](#calling-functions) {#calling-functions}
95
96Let's try calling a function:
97
98<pre><samp><span class="repl-prompt">Str.concat("Hi ", "there.")</span>
99
100<span class="literal">"Hi there."</span> <span class="colon">:</span> Str
101</samp></pre>
102
103Here we're calling the `Str.concat` function and passing two arguments: the string `"Hi "` and the string `"there."`. This _concatenates_ the two strings together (that is, it puts one after the other) and returns the resulting combined string of `"Hi there."`.
104
105The `Str.concat` function has a dot in its name. In `Str.concat`, `Str` is the name of a _module_, and `concat` is the name of a function inside that module.
106
107We'll get into more depth about modules later, but for now you can think of a module as a named collection of functions. Eventually we'll discuss how to use them for more than that.
108
109### [String Interpolation](#string-interpolation) {#string-interpolation}
110
111An alternative syntax for `Str.concat` is _string interpolation_, which looks like this:
112
113<pre><samp class="repl-prompt"><span class="literal">"<span class="str-esc">${</span><span class="str-interp">greeting</span><span class="str-esc">}</span> there, <span class="str-esc">${</span><span class="str-interp">audience</span><span class="str-esc">}</span>."</span></samp></pre>
114
115This is syntax sugar for calling `Str.concat` several times, like so:
116
117```roc
118Str.concat(greeting, Str.concat(" there, ", Str.concat(audience, ".")))
119```
120
121You can put entire single-line expressions inside the parentheses in string interpolation. For example:
122
123<pre><samp class="repl-prompt"><span class="literal">"Two plus three is: <span class="str-esc">${</span><span class="str-interp">Num.to_str(2 + 3)</span><span class="str-esc">}</span>"</span></samp></pre>
124
125By the way, there are many other ways to put strings together! Check out the [documentation](https://www.roc-lang.org/builtins/Str) for the `Str` module for more.
126
127## [Building an Application](#building-an-application) {#building-an-application}
128
129Let's move out of the REPL and create our first Roc application!
130
131Make a file named `main.roc` and put this in it:
132
133```roc
134app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br" }
135
136import pf.Stdout
137
138main! = |_args|
139 Stdout.line!("Hi there, from inside a Roc app. π")
140```
141
142Try running this with:
143
144<samp>roc main.roc</samp>
145
146You should see a message about a file being downloaded, followed by this:
147
148<samp>Hi there, from inside a Roc app. π</samp>
149
150Congratulations, you've written your first Roc application! We'll go over what the parts above `main` do later, but let's play around a bit first.
151
152### [Defs](#defs) {#defs}
153
154Try replacing the `main` line with this:
155
156```roc
157birds = 3
158
159iguanas = 2
160
161total = Num.to_str(birds + iguanas)
162
163main! = |_args|
164 Stdout.line!("There are ${total} animals.")
165```
166
167Now run `roc main.roc` again. This time the "Downloading ..." message won't appear; the file has been cached from last time, and won't need to be downloaded again.
168
169You should see this:
170
171<samp>There are 5 animals.</samp>
172
173`main.roc` now has four definitions (_defs_ for short) `birds`, <span class="nowrap">`iguanas`,</span> `total`, and `main!`.
174
175A definition names an expression.
176
177- The first two defs assign the names `birds` and `iguanas` to the expressions `3` and `2`.
178- The next def assigns the name `total` to the expression `Num.to_str(birds + iguanas)`.
179
180Once we have a def, we can use its name in other expressions. For example, the `total` expression refers to `birds` and `iguanas`, and `Stdout.line!("There are ${total} animals.")` refers to `total`.
181
182You can name a def using any combination of letters and numbers, but they have to start with a lowercase letter.
183
184**Note:** Defs are constant; they can't be reassigned. We'd get an error if we wrote these two defs in the same scope:
185
186```roc
187birds = 3
188birds = 2
189```
190
191### [Defining Functions](#defining-functions) {#defining-functions}
192
193So far we've called functions like `Num.to_str`, `Str.concat`, and `Stdout.line`. Next let's try defining a function of our own.
194
195```roc
196birds = 3
197
198iguanas = 2
199
200total = add_and_stringify(birds, iguanas)
201
202main! = |_args|
203 Stdout.line!("There are ${total} animals.")
204
205add_and_stringify = |num1, num2|
206 Num.to_str(num1 + num2)
207```
208
209This new `add_and_stringify` function we've defined accepts two numbers, adds them, calls `Num.to_str` on the result, and returns that.
210
211The `|num1, num2|` syntax defines a function's arguments, and the expression after the final `|` is the body of the function. Whenever a function gets called, its body expression gets evaluated and returned.
212
213### [`if`-`then`-`else` expressions](#if-then-else) {#if-then-else}
214
215Let's modify this function to return an empty string if the numbers add to zero.
216
217```roc
218add_and_stringify = |num1, num2|
219 sum = num1 + num2
220
221 if sum == 0 then
222 ""
223 else
224 Num.to_str(sum)
225```
226
227We did two things here:
228
229- We introduced a _local def_ named `sum`, and set it equal to `num1 + num2`. Because we defined `sum` inside `add_and_stringify`, it's _local_ to that scope and can't be accessed outside that function.
230- We added an `if`\-`then`\-`else` conditional to return either `""` or `Num.to_str(sum)` depending on whether `sum == 0`.
231
232Every `if` must be accompanied by both `then` and also `else`. Having an `if` without an `else` is an error, because `if` is an expression, and all expressions must evaluate to a value. If there were ever an `if` without an `else`, that would be an expression that might not evaluate to a value!
233
234### [`else if` expressions](#else-if) {#else-if}
235
236We can combine `if` and `else` to get `else if`, like so:
237
238```roc
239add_and_stringify = |num1, num2|
240 sum = num1 + num2
241
242 if sum == 0 then
243 ""
244 else if sum < 0 then
245 "negative"
246 else
247 Num.to_str(sum)
248```
249
250Note that `else if` is not a separate language keyword! It's just an `if`/`else` where the `else` branch contains another `if`/`else`. This is easier to see with different indentation:
251
252```roc
253add_and_stringify = |num1, num2|
254 sum = num1 + num2
255
256 if sum == 0 then
257 ""
258 else
259 if sum < 0 then
260 "negative"
261 else
262 Num.to_str(sum)
263```
264
265This differently-indented version is equivalent to writing `else if sum < 0 then` on the same line, although the convention is to use the original version's style.
266
267### [Comments](#comments) {#comments}
268
269This is a comment in Roc:
270
271```roc
272# The 'name' field is unused by add_and_stringify
273```
274
275Whenever you write `#` it means that the rest of the line is a comment, and will not affect the
276running program. Roc does not have multiline comment syntax.
277
278### [Doc Comments](#doc-comments) {#doc-comments}
279
280Comments that begin with `##` are "doc comments" which will be included in generated documentation (`roc docs`). They can include code blocks by adding five spaces after `##`.
281
282```roc
283## This is a comment for documentation, and includes a code block.
284##
285## x = 2
286## expect x == 2
287```
288
289Like other comments, doc comments do not affect the running program.
290
291### [Records](#records) {#records}
292
293Currently our `add_and_stringify` function takes two arguments. We can instead make it take one argument like so:
294
295```roc
296total = add_and_stringify({ birds: 5, iguanas: 7 })
297
298add_and_stringify = |counts|
299 Num.to_str(counts.birds + counts.iguanas)
300```
301
302The function now takes a _record_, which is a group of named values. Records are not [objects](<https://en.wikipedia.org/wiki/Object_(computer_science)>); they don't have methods or inheritance, they just store information.
303
304The expression `{ birds: 5, iguanas: 7 }` defines a record with two _fields_ (the `birds` field and the `iguanas` field) and then assigns the number `5` to the `birds` field and the number `7` to the `iguanas` field. Order doesn't matter with record fields; we could have also specified `iguanas` first and `birds` second, and Roc would consider it the exact same record.
305
306When we write `counts.birds`, it accesses the `birds` field of the `counts` record, and when we write `counts.iguanas` it accesses the `iguanas` field.
307
308When we use [`==`](/builtins/Bool#is_eq) on records, it compares all the fields in both records with [`==`](/builtins/Bool#is_eq), and only considers the two records equal if all of their fields are equal. If one record has more fields than the other, or if the types associated with a given field are different between one field and the other, the Roc compiler will give an error at build time.
309
310> **Note:** Some other languages have a concept of "identity equality" that's separate from the "structural equality" we just described. Roc does not have a concept of identity equality; this is the only way equality works!
311
312### [Accepting extra fields](#accepting-extra-fields) {#accepting-extra-fields}
313
314The `add_and_stringify` function will accept any record with at least the fields `birds` and `iguanas`, but it will also accept records with more fields. For example:
315
316```roc
317total = add_and_stringify({ birds: 5, iguanas: 7 })
318
319# The `note` field is unused by add_and_stringify
320total_with_note = add_and_stringify({ birds: 4, iguanas: 3, note: "Whee!" })
321
322add_and_stringify = |counts|
323 Num.to_str(counts.birds + counts.iguanas)
324```
325
326This works because `add_and_stringify` only uses `counts.birds` and `counts.iguanas`. If we were to use `counts.note` inside `add_and_stringify`, then we would get an error because `total` is calling `add_and_stringify` passing a record that doesn't have a `note` field.
327
328### [Record shorthands](#record-shorthands) {#record-shorthands}
329
330Roc has a couple of shorthands you can use to express some record-related operations more concisely.
331
332Instead of writing `|record| record.x` we can write `.x` and it will evaluate to the same thing: a function that takes a record and returns its `x` field. You can do this with any field you want. For example:
333
334```roc
335# return_foo is a function that takes a record
336# and returns the `foo` field of that record.
337return_foo = .foo
338
339return_foo({ foo: "hi!", bar: "blah" })
340# returns "hi!"
341```
342
343Sometimes we assign a def to a field that happens to have the same nameβfor example, `{ x: x }`.
344In these cases, we shorten it to writing the name of the def aloneβfor example, `{ x }`. We can do this with as many fields as we like; here are several different ways to define the same record:
345
346- `{ x: x, y: y }`
347- `{ x, y }`
348- `{ x: x, y }`
349- `{ x, y: y }`
350
351### [Record destructuring](#record-destructuring) {#record-destructuring}
352
353We can use _destructuring_ to avoid naming a record in a function argument, instead giving names to its individual fields:
354
355```roc
356add_and_stringify = |{ birds, iguanas }|
357 Num.to_str(birds + iguanas)
358```
359
360Here, we've _destructured_ the record to create a `birds` def that's assigned to its `birds` field, and an `iguanas` def that's assigned to its `iguanas` field. We can customize this if we like:
361
362```roc
363add_and_stringify = |{ birds, iguanas: lizards }|
364 Num.to_str(birds + lizards)
365```
366
367In this version, we created a `lizards` def that's assigned to the record's `iguanas` field. (We could also do something similar with the `birds` field if we like.)
368
369Finally, destructuring can be used in defs too:
370
371```roc
372{ x, y } = { x: 5, y: 10 }
373```
374
375### [Making records from other records](#making-records-from-other-records) {#making-records-from-other-records}
376
377So far we've only constructed records from scratch, by specifying all of their fields. We can also construct new records by using another record to use as a starting point, and then specifying only the fields we want to be different. For example, here are two ways to get the same record:
378
379```roc
380original = { birds: 5, zebras: 2, iguanas: 7, goats: 1 }
381from_scratch = { birds: 4, zebras: 2, iguanas: 3, goats: 1 }
382from_original = { original & birds: 4, iguanas: 3 }
383```
384
385The `from_scratch` and `from_original` records are equal, although they're defined in different ways.
386
387- `from_scratch` was built using the same record syntax we've been using up to this point.
388- `from_original` created a new record using the contents of `original` as defaults for fields that it didn't specify after the `&`.
389
390Note that `&` can't introduce new fields to a record, or change the types of existing fields.
391(Trying to do either of these will result in an error at build time!)
392
393### [Debugging with `dbg`](#dbg) {#dbg}
394
395[Print debugging](https://en.wikipedia.org/wiki/Debugging#Techniques) is the most common debugging technique in the history of programming, and Roc has a `dbg` keyword to facilitate it. Here's an example of how to use `dbg`:
396
397```roc
398pluralize = |singular, plural, count|
399 dbg count
400
401 if count == 1 then
402 singular
403 else
404 plural
405```
406
407Whenever this `dbg` line of code is reached, the value of `count` will be printed to [stderr](<https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)>), along with the source code file and line number where the `dbg` itself was written:
408
409<samp><span class="kw">[pluralize.roc 6:8]</span> 5</samp>
410
411Here, `[pluralize.roc 6:8]` tells us that this `dbg` was written in the file `pluralize.roc` on line 6, column 8.
412
413You can give `dbg` any expression you like, for example:
414
415```roc
416dbg Str.concat(singular, plural)
417```
418
419You can also use `dbg` as a function inside an expression, which will print the function argument to stderr and then return the argument to the caller. For example:
420
421```roc
422inc = |n| 1 + dbg n
423```
424
425### [Tuples](#tuples) {#tuples}
426
427One way to have `dbg` print multiple values at a time is to wrap them in a record:
428
429```roc
430dbg { text: "the value of count is:", value: count }
431```
432
433A more concise way would be to wrap them in a _tuple_, like so:
434
435```roc
436dbg ("the value of count is:", count)
437```
438
439Visually, tuples in Roc look like lists (but with parentheses instead of square brackets). However, a tuple is much more like a record - in fact, tuples and records compile down to the exact same representation at runtime! So anywhere you would use a tuple, you can use a record instead, and the in-memory representation will be exactly the same.
440
441Like records, tuples are fixed-length and can't be iterated over. Also, they can contain values of different types. The difference is that in a record, each field is labeled (and their position doesn't matter), whereas in a tuple, each field is specified by its position.
442
443### [Accessing values in tuples](#tuple-access) {#tuple-access}
444
445Just like how there are two ways to access a record's fields (namely, the `.` operator and [record destructuring](#record-destructuring)), there are also two similar ways to access tuple fields:
446
447```roc
448# tuple field access
449tuple = ("hello", 42, ["list"])
450
451first = tuple.0 # "hello"
452second = tuple.1 # 42
453third = tuple.2 # ["list"]
454```
455
456```roc
457# tuple destructuring
458(first, second, third) = ("hello", 42, ["list"])
459```
460
461<details>
462 <summary>Pronouncing Tuple</summary>
463 By the way, there are two common ways to pronounce "tuple"βone sounds like "two-pull" and the other rhymes with "supple"βand although no clear consensus has emerged in the programming world, people seem generally accepting when others pronounce it differently than they do.
464</details>
465
466## [Pattern Matching](#pattern-matching) {#pattern-matching}
467
468Sometimes we want to represent that something can have one of several values. For example:
469
470```roc
471stoplight_color =
472 if something > 0 then
473 Red
474 else if something == 0 then
475 Yellow
476 else
477 Green
478```
479
480Here, `stoplight_color` 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_.
481
482### [Tags](#tags) {#tags}
483
484A 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`.
485
486Let's say we wanted to turn `stoplight_color` from a `Red`, `Green`, or `Yellow` into a string. Here's one way we could do that:
487
488```roc
489stoplight_str =
490 if stoplight_color == Red then
491 "red"
492 else if stoplight_color == Green then
493 "green"
494 else
495 "yellow"
496```
497
498We can express this logic more concisely using `when`/`is` instead of `if`/`then`:
499
500```roc
501stoplight_str =
502 when stoplight_color is
503 Red -> "red"
504 Green -> "green"
505 Yellow -> "yellow"
506```
507
508This results in the same value for `stoplight_str`. 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 `stoplight_color`, and then we specify the different things we're comparing it to: `Red`, `Green`, and `Yellow`.
509
510Besides being more concise, there are other advantages to using `when` here.
511
5121. We don't have to specify an `else` branch, so the code can be more self-documenting about exactly what all the options are.
5132. 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 `stoplight_color` could be `Green`. It knows this because `Green` is one of the possibilities in our `stoplight_color = if ...` definition.
514
515We can still have the equivalent of an `else` branch in our `when` if we like. Instead of writing `else`, we write `_ ->` like so:
516
517```roc
518stoplight_str =
519 when stoplight_color is
520 Red -> "red"
521 _ -> "not red"
522```
523
524This 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 `stoplight_color` 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!
525
526We can make this `when` _exhaustive_ (that is, covering all possibilities) without using `_ ->` by using `|` to specify multiple matching conditions for the same branch:
527
528```roc
529stoplight_str =
530 when stoplight_color is
531 Red -> "red"
532 Green | Yellow -> "not red"
533```
534
535You can read `Green | Yellow` as "either `Green` or `Yellow`". By writing it this way, if we introduce the possibility that `stoplight_color` 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.
536
537We can also combine `if` and `when` to make branches more specific:
538
539```roc
540stoplight_str =
541 when stoplight_color is
542 Red -> "red"
543 Green | Yellow if contrast > 75 -> "not red, but very high contrast"
544 Green | Yellow if contrast > 50 -> "not red, but high contrast"
545 Green | Yellow -> "not red"
546```
547
548This will give the same answer for `stoplight_str` as if we had written the following:
549
550```roc
551stoplight_str =
552 when stoplight_color is
553 Red -> "red"
554 Green | Yellow ->
555 if contrast > 75 then
556 "not red, but very high contrast"
557 else if contrast > 50 then
558 "not red, but high contrast"
559 else
560 "not red"
561```
562
563Either style can be a reasonable choice depending on the circumstances.
564
565### [Tags with payloads](#tags-with-payloads) {#tags-with-payloads}
566
567Tags can have _payloads_βthat is, values inside them. For example:
568
569```roc
570stoplight_color =
571 if something > 100 then
572 Red
573 else if something > 0 then
574 Yellow
575 else if something == 0 then
576 Green
577 else
578 Custom("some other color")
579
580stoplight_str =
581 when stoplight_color is
582 Red -> "red"
583 Green | Yellow -> "not red"
584 Custom(description) -> description
585```
586
587This makes two changes to our earlier `stoplight_color` / `stoplight_str` example.
588
5891. We sometimes chose to set `stoplight_color` to be `Custom("some other color")`. When we did this, we gave the `Custom` tag a _payload_ of the string `"some other color"`.
5902. 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 a def or a function argument.
591
592Any 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.
593
594A tag can also have a payload with more than one value. Instead of `Custom({ r: 40, g: 60, b: 80 })` we could write `Custom(40, 60, 80)`. If we did that, then instead of destructuring a record with `Custom({ r, g, b }) ->` inside a `when`, we would write `Custom(r, g, b) ->` to destructure the values directly out of the payload.
595
596We 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) ->`.
597
598### [Booleans](#booleans) {#booleans}
599
600In many programming languages, `true` and `false` are special language keywords that refer to the two [boolean](https://en.wikipedia.org/wiki/Boolean_data_type) values. In Roc, booleans do not get special keywords; instead, they are exposed as the ordinary values `Bool.true` and `Bool.false`.
601
602This design is partly to keep the number of special keywords in the language smaller, but mainly to suggest how booleans are intended to be used in Roc: for [_boolean logic_](https://en.wikipedia.org/wiki/Boolean_algebra) (`&&`, `||`, and so on) as opposed to for data modeling. Tags are the preferred choice for data modeling, and having tag values be more concise than boolean values helps make this preference clear.
603
604As an example of why tags are encouraged for data modeling, in many languages it would be common to write a record like `{ name: "Richard", is_admin: Bool.true }`, but in Roc it would be preferable to write something like `{ name: "Richard", role: Admin }`. At first, the `role` field might only ever be set to `Admin` or `Normal`, but because the data has been modeled using tags instead of booleans, it's much easier to add other alternatives in the future, like `Guest` or `Moderator` - some of which might also want payloads.
605
606### [Lists](#lists) {#lists}
607
608Another thing we can do in Roc is to make a _list_ of values. Here's an example:
609
610```roc
611names = ["Sam", "Lee", "Ari"]
612```
613
614This is a list with three elements in it, all strings. We can add a fourth element using `List.append` like so:
615
616```roc
617List.append(names, "Jess")
618```
619
620This returns a **new** list with `"Jess"` after `"Ari"`, and doesn't modify the original list at all. All values in Roc (including lists, but also records, strings, numbers, and so on) are immutable, meaning whenever we want to "change" them, we want to instead pass them to a function which returns some variation of what was passed in.
621
622### [List.map](#list-map) {#list-map}
623
624A common way to transform one list into another is to use `List.map`. Here's an example of how to use it:
625
626```roc
627List.map([1, 2, 3], |num| num * 2)
628```
629
630This returns `[2, 4, 6]`.
631
632`List.map` takes two arguments:
633
6341. An input list
6352. A function that will be called on each element of that list
636
637It then returns a list which it creates by calling the given function on each element in the input list. In this example, `List.map` calls the function `|num| num * 2` on each element in `[1, 2, 3]` to get a new list of `[2, 4, 6]`.
638
639We can also give `List.map` a named function, instead of an anonymous one:
640
641```roc
642List.map([1, 2, 3], Num.is_odd)
643```
644
645This `Num.is_odd` function returns `Bool.true` if it's given an odd number, and `Bool.false` otherwise. So `Num.is_odd(5)` returns `Bool.true` and `Num.is_odd(2)` returns `Bool.false`.
646
647As such, calling `List.map([1, 2, 3], Num.is_odd)` returns a new list of `[Bool.true, Bool.false, Bool.true]`.
648
649### [List element type compatibility](#list-element-type-compatibility) {#list-element-type-compatibility}
650
651If we tried to give `List.map` a function that didn't work on the elements in the list, then we'd get an error at compile time. Here's a valid, and then an invalid example:
652
653```roc
654# working example
655List.map([-1, 2, 3, -4], Num.is_negative)
656# returns [Bool.true, Bool.false, Bool.false, Bool.true]
657```
658
659```roc
660# invalid example
661List.map(["A", "B", "C"], Num.is_negative)
662# error: is_negative doesn't work on strings!
663```
664
665Because `Num.is_negative` works on numbers and not strings, calling `List.map` with `Num.is_negative` and a list of numbers works, but doing the same with a list of strings doesn't work.
666
667This wouldn't work either:
668
669```roc
670List.map(["A", "B", "C", 1, 2, 3], Num.is_negative)
671```
672
673Every element in a Roc list has to share the same type. For example, we can have a list of strings like `["Sam", "Lee", "Ari"]`, or a list of numbers like `[1, 2, 3, 4, 5]` but we can't have a list which mixes strings and numbers like `["Sam", 1, "Lee", 2, 3]`, that would be a compile-time error.
674
675Ensuring that all elements in a list share a type eliminates entire categories of problems. For example, it means that whenever you use `List.append` to add elements to a list, as long as you don't have any compile-time errors, you won't get any runtime errors from calling `List.map` afterwards, no matter what you appended to the list! More generally, it's safe to assume that unless you run out of memory, `List.map` will run successfully unless you got a compile-time error about an incompatibility (like `Num.neg` on a list of strings).
676
677### [Lists that hold elements of different types](#lists-that-hold-elements-of-different-types) {#lists-that-hold-elements-of-different-types}
678
679We can use tags with payloads to make a list that contains a mixture of different types. For example:
680
681```roc
682List.map([StrElem "A", StrElem "b", NumElem 1, StrElem "c", NumElem -3], |elem|
683 when elem is
684 NumElem(num) -> Num.is_negative(num)
685 StrElem(str) -> Str.starts_with(str, "A")
686)
687# returns [Bool.true, Bool.false, Bool.false, Bool.false, Bool.true]
688```
689
690Compare this with the example from earlier, which caused a compile-time error:
691
692```roc
693List.map(["A", "B", "C", 1, 2, 3], Num.is_negative)
694```
695
696The version that uses tags works because we aren't trying to call `Num.is_negative` on each element. Instead, we're using a `when` to tell when we've got a string or a number, and then calling either `Num.is_negative` or `Str.starts_with` depending on which type we have.
697
698We could take this as far as we like, adding more different tags (e.g. `BoolElem(Bool.true)`) and then adding more branches to the `when` to handle them appropriately.
699
700### [Using tags as functions](#using-tags-as-functions) {#using-tags-as-functions}
701
702Let's say I want to apply a tag to a bunch of elements in a list. For example:
703
704```roc
705List.map(["a", "b", "c"], |str| Foo(str))
706```
707
708This is a perfectly reasonable way to write it, but I can also write it like this:
709
710```roc
711List.map(["a", "b", "c"], Foo)
712```
713
714These two versions compile to the same thing. As a convenience, Roc lets you specify a tag name where a function is expected; when you do this, the compiler infers that you want a function which uses all of its arguments as the payload to the given tag.
715
716### [List.any and List.all](#list-any-and-list-all) {#list-any-and-list-all}
717
718There are several functions that work like `List.map`, they walk through each element of a list and do something with it. Another is `List.any`, which returns `Bool.true` if calling the given function on any element in the list returns `Bool.true`:
719
720```roc
721List.any([1, 2, 3], Num.is_odd)
722# returns `Bool.true` because 1 and 3 are odd
723```
724
725```roc
726List.any([1, 2, 3], Num.is_negative)
727# returns `Bool.false` because none of these is negative
728```
729
730There's also `List.all` which only returns `Bool.true` if all the elements in the list pass the test:
731
732```roc
733List.all([1, 2, 3], Num.is_odd)
734# returns `Bool.false` because 2 is not odd
735```
736
737```roc
738List.all([1, 2, 3], Num.is_positive)
739# returns `Bool.true` because all of these are positive
740```
741
742### [Removing elements from a list](#removing-elements-from-a-list) {#removing-elements-from-a-list}
743
744You can also drop elements from a list. One way is `List.drop_at` - for example:
745
746```roc
747List.drop_at(["Sam", "Lee", "Ari"], 1)
748# drops the element at offset 1 ("Lee") and returns ["Sam", "Ari"]
749```
750
751Another way is to use `List.keep_if`, which passes each of the list's elements to the given function, and then keeps them only if that function returns `Bool.true`.
752
753```roc
754List.keep_if([1, 2, 3, 4, 5], Num.is_even)
755# returns [2, 4]
756```
757
758There's also `List.drop_if`, which does the opposite:
759
760```roc
761List.drop_if([1, 2, 3, 4, 5], Num.is_even)
762# returns [1, 3, 5]
763```
764
765### [Getting an individual element from a list](#getting-an-individual-element-from-a-list) {#getting-an-individual-element-from-a-list}
766
767Another thing we can do with a list is to get an individual element out of it. `List.get` is a common way to do this; it takes a list and an index, and then returns the element at that index... if there is one. But what if there isn't?
768
769For example, what do each of these return?
770
771```roc
772List.get(["a", "b", "c"], 1)
773```
774
775```roc
776List.get(["a", "b", "c"], 100)
777```
778
779The answer is that the first one returns `Ok "b"` and the second one returns `Err(OutOfBounds)`. They both return tags! This is done so that the caller becomes responsible for handling the possibility that the index is outside the bounds of that particular list.
780
781Here's how calling `List.get` can look in practice:
782
783```roc
784when List.get(["a", "b", "c"], index) is
785 Ok(str) -> "I got this string: ${str}"
786 Err(OutOfBounds) -> "That index was out of bounds, sorry!"
787```
788
789There's also `List.first`, which always gets the first element, and `List.last` which always gets the last. They return `Err(ListWasEmpty)` instead of `Err(OutOfBounds)`, because the only way they can fail is if you pass them an empty list!
790
791### [Error Handling](#error-handling) {#error-handling}
792
793The `List` functions such as `List.get`, `List.first`, and `List.last` demonstrate a common pattern in Roc: operations that can fail returning either an `Ok` tag with the answer (if successful), or an `Err` tag with another tag describing what went wrong (if unsuccessful). In fact, it's such a common pattern that there's a whole module called `Result` which deals with these two tags. Here are some examples of `Result` functions:
794
795```roc
796Result.with_default(List.get(["a", "b", "c"], 100), "")
797# returns "" because that's the default we said to use if List.get returned an Err
798```
799
800```roc
801Result.isOk(List.get(["a", "b", "c"], 1))
802# returns `Bool.true` because `List.get` returned an `Ok` tag. (The payload gets ignored.)
803
804# Note: There's a Result.isErr function that works similarly.
805```
806
807```roc
808# Running this will produce `Ok("c")`
809Result.try(Str.to_u64("2"), list_get)
810
811list_get : U64 -> Result Str [OutOfBounds]
812list_get = |index|
813 List.get(["a", "b", "c", "d"], index)
814
815# Notes:
816# - `Str.to_u64("2")` parses the string "2" to the integer 2, and returns `Ok(2)` (more on
817# integer types later)
818# - since parsing is successful, `Result.try` passes 2 to the `list_get` function
819# - passing "abc" or "1000" instead of "2" would have resulted in `Err(InvalidNumStr)`
820# or `Err(OutOfBounds)` respectively
821```
822
823`Result.try` is often used to chain two functions that return `Result` (as in the example above). This prevents you from needing to add error handling code at every intermediate step.
824
825### [The `?` postfix operator](#the-question-postfix-operator) {#the-question-postfix-operator}
826
827Roc also has a `?` postfix operator, which is convenient syntax sugar for `Result.try`, but avoids a lot of noise introduced by callbacks.
828For example, consider the following `get_letter` function:
829
830```roc
831get_letter : Str -> Result Str [OutOfBounds, InvalidNumStr]
832get_letter = |index_str|
833 index = Str.to_u64(index_str)?
834 List.get(["a", "b", "c", "d"], index)
835```
836
837Here's what this does:
838
839- If the `Str.to_u64` function returns an `Ok` value, then `?` will return what's inside the `Ok`. For example:
840 - If we call `get_letter("2")`, then `Str.to_u64` returns `Ok(2)`, and the `?` unwraps to the integer 2, so `index` is set to 2 (not `Ok(2)`). Then the `List.get` function is called and returns `Ok("c")`.
841 - If the `Str.to_u64` function returns an `Err` value, then the `?` operator immediately interrupts the `get_letter` function and makes it return this error.
842 - For example, if we call `get_letter("abc")`, then the call to `Str.to_u64` returns `Err(InvalidNumStr)`, and the `?` keyword ensures that the `get_letter` function returns this error immediately, without executing the rest of the function.
843
844Thanks to the `?` postfix operator, your code can focus on the "happy path" (where nothing fails) and simply bubble up to the caller any error that might occur. Your error handling code can be neatly separated, and you can rest assured that you won't forget to handle any errors, since the compiler will let you know. See this [code example](https://www.roc-lang.org/examples/ErrorHandlingBasic/README) for more details on error handling.
845
846### [Recovering from errors with the `??` infix operator](#recovering-from-errors) {#recovering-from-errors}
847
848Above you saw this code sample to show how to recover from errors using the
849`Result.with_default` function:
850
851```roc
852Result.with_default(List.get(["a", "b", "c"], 100), "")
853# returns "" because that's the default we said to use if List.get returned an Err
854```
855
856But this was common enough that Roc has syntax to make it easier to read and
857write:
858
859```roc
860List.get(["a", "b", "c"], 100) ?? ""
861# returns "" because that's the default we said to use if List.get returned an Err
862```
863
864All in all, Roc recognizes that handling errors is important - and makes working
865with them as values as painless as possible.
866
867Now let's get back to lists!
868
869### [Walking the elements in a list](#walking-the-elements-in-a-list) {#walking-the-elements-in-a-list}
870
871We've now seen a few different ways you can transform lists. Sometimes, though, there's nothing
872that quite does what you want, and you might find yourself calling `List.get` repeatedly to
873retrieve every element in the list and use it to build up the new value you want. That approach
874can work, but it has a few downsides:
875
876- Each `List.get` call returns a `Result` that must be dealt with, even though you plan to use every element in the list anyway
877- There's a runtime performance overhead associated with each of these `Result`s, which you won't find in other "look at every element in the list" operations like `List.keep_if`.
878- It's more verbose than the alternative we're about to discuss
879
880The `List.walk` function gives you a way to walk over the elements in a list and build up whatever
881return value you like. It's a great alternative to calling `List.get` on every element in the list
882because it's more concise, runs faster, and doesn't give you any `Result`s to deal with.
883
884Here's an example:
885
886```roc
887List.walk([1, 2, 3, 4, 5], { evens: [], odds: [] }, |state, elem|
888 if Num.is_even(elem) then
889 { state & evens: List.append(state.evens, elem) }
890 else
891 { state & odds: List.append(state.odds, elem) }
892)
893
894# returns { evens: [2, 4], odds: [1, 3, 5] }
895```
896
897In this example, we walk over the list `[1, 2, 3, 4, 5]` and add each element to either the `evens` or `odds` field of a `state` record: `{ evens, odds }`. By the end, that record has a list of all the even numbers in the list and a list of all the odd numbers.
898
899`List.walk` takes a few ingredients:
900
9011. A list. (`[1, 2, 3, 4, 5]`)
9022. An initial `state` value. (`{ evens: [], odds: [] }`)
9033. A function which takes the current `state` and element, and returns a new `state`. (`|state, elem| ...`)
904
905It then proceeds to walk over each element in the list and call that function. Each time, the state that function returns becomes the argument to the next function call. Here are the arguments the function will receive, and what it will return, as `List.walk` walks over the list `[1, 2, 3, 4, 5]`:
906
907| State | Element | Return Value |
908| --------------------------------- | ------- | ------------------------------------ |
909| `{ evens: [], odds: [] }` | `1` | `{ evens: [], odds: [1] }` |
910| `{ evens: [], odds: [1] }` | `2` | `{ evens: [2], odds: [1] }` |
911| `{ evens: [2], odds: [1] }` | `3` | `{ evens: [2], odds: [1, 3] }` |
912| `{ evens: [2], odds: [1, 3] }` | `4` | `{ evens: [2, 4], odds: [1, 3] }` |
913| `{ evens: [2, 4], odds: [1, 3] }` | `5` | `{ evens: [2, 4], odds: [1, 3, 5] }` |
914
915Note that the initial `state` argument is `{ evens: [], odds: [] }` because that's the argument
916we passed `List.walk` for its initial state. From then on, each `state` argument is whatever the
917previous function call returned.
918
919Once the list has run out of elements, `List.walk` returns whatever the final function call returnedβin this case, `{ evens: [2, 4], odds: [1, 3, 5] }`. (If the list was empty, the function never gets called and `List.walk` returns the initial state.)
920
921Note that the state doesn't have to be a record; it can be anything you want. For example, if you made it a `Bool`, you could implement `List.any` using `List.walk`. You could also make the state be a list, and implement `List.map`, `List.keep_if`, or `List.drop_if`. There are a lot of things you can do with `List.walk`!
922
923A helpful way to remember the argument order for `List.walk` is that that its arguments follow the same pattern as what we've seen with `List.map`, `List.any`, `List.keep_if`, and `List.drop_if`: the first argument is a list, and the last argument is a function. The difference here is that `List.walk` has one more argument than those other functions; the only place it could go while preserving that pattern is in the middle!
924
925> **Note:** Other languages give this operation different names, such as `fold`, `reduce`, `accumulate`, `aggregate`, `compress`, and `inject`. Consider using one of the following if you would like to call an effectful function on a list of
926
927```roc
928List.for_each! : List a, (a => {}) => {}
929List.for_each_try! : List a, (a => Result {} err) => Result {} err
930```
931
932### [Pattern Matching on Lists](#pattern-matching-on-lists) {#pattern-matching-on-lists}
933
934You can also pattern match on lists, like so:
935
936```roc
937when my_list is
938 [] -> 0 # the list is empty
939 [Foo, ..] -> 1 # it starts with a Foo tag
940 [_, ..] -> 2 # it contains at least one element, which we ignore
941 [Foo, Bar, ..] -> 3 # it starts with a Foo tag followed by a Bar tag
942 [Foo, Bar, Baz] -> 4 # it has exactly 3 elements: Foo, Bar, and Baz
943 [Foo, a, ..] -> 5 # its first element is Foo, and its second we name `a`
944 [Ok a, ..] -> 6 # it starts with an Ok containing a payload named `a`
945 [.., Foo] -> 7 # it ends with a Foo tag
946 [A, B, .., C, D] -> 8 # it has certain elements at the beginning and end
947 [head, .. as tail] -> 9 # destructure a list into a first element (head) and the rest (tail)
948```
949
950This can be both more concise and more efficient (at runtime) than calling [`List.get`](https://www.roc-lang.org/builtins/List#get) multiple times, since each call to `get` requires a separate conditional to handle the different `Result`s they return.
951
952> **Note:** Each list pattern can only have one `..`, which is known as the "rest pattern" because it's where the _rest_ of the list goes.
953
954See the [Pattern Matching example](https://www.roc-lang.org/examples/PatternMatching/README.html) which shows different ways to do pattern matching in Roc using tags, strings, and numbers.
955
956### [The pipe operator](#the-pipe-operator) {#the-pipe-operator}
957
958When you have nested function calls, sometimes it can be clearer to write them in a "pipelined" style using the `|>` operator. Here are three examples of writing the same expression; they all compile to exactly the same thing, but two of them use the `|>` operator to change how the calls look.
959
960```roc
961Result.with_default(List.get(["a", "b", "c"], 1), "")
962```
963
964```roc
965List.get(["a", "b", "c"], 1)
966|> Result.with_default("")
967```
968
969The `|>` operator takes the value that comes before the `|>` and passes it as the first argument to whatever comes after the `|>`. So in the example above, the `|>` takes `List.get(["a", "b", "c"], 1)` and passes that value as the first argument to `Result.with_default`, making `""` the second argument to `Result.with_default`.
970
971We can take this a step further like so:
972
973```roc
974["a", "b", "c"]
975|> List.get(1)
976|> Result.with_default("")
977```
978
979This is still equivalent to the first expression. Since `|>` is known as the "pipe operator," we can read this as "start with `["a", "b", "c"]`, then pipe it to `List.get`, then pipe it to `Result.with_default`."
980
981One reason the `|>` operator injects the value as the first argument is to make it work better with functions where argument order matters. For example, these two uses of `List.append` are equivalent:
982
983```roc
984List.append(["a", "b", "c"], "d")
985```
986
987```roc
988["a", "b", "c"]
989|> List.append("d")
990```
991
992Another example is `Num.div`. All three of the following do the same thing, because `a / b` in Roc is syntax sugar for `Num.div(a, b)`:
993
994```roc
995first / second
996```
997
998```roc
999Num.div(first, second)
1000```
1001
1002```roc
1003first |> Num.div(second)
1004```
1005
1006All operators in Roc are syntax sugar for normal function calls. See the [Operator Desugaring Table](https://www.roc-lang.org/tutorial#operator-desugaring-table) at the end of this tutorial for a complete list of them.
1007
1008## [Types](#types) {#types}
1009
1010Sometimes you may want to document the type of a definition. For example, you might write:
1011
1012```roc
1013# Takes a first_name string and a last_name string, and returns a string
1014full_name = |first_name, last_name|
1015 "${first_name} ${last_name}"
1016```
1017
1018Comments can be valuable documentation, but they can also get out of date and become misleading. If someone changes this function and forgets to update the comment, it will no longer be accurate.
1019
1020### [Type Annotations](#type-annotations) {#type-annotations}
1021
1022Here's another way to document this function's type, which doesn't have that problem:
1023
1024```roc
1025full_name : Str, Str -> Str
1026full_name = |first_name, last_name|
1027 "${first_name} ${last_name}"
1028```
1029
1030The `full_name :` line is a _type annotation_. It's a strictly optional piece of metadata we can add above a def to describe its type. Unlike a comment, the Roc compiler will check type annotations for accuracy. If the annotation ever doesn't fit with the implementation, we'll get a compile-time error.
1031
1032The annotation `full_name : Str, Str -> Str` says "`full_name` is a function that takes two strings as arguments and returns a string."
1033
1034We can give type annotations to any value, not just functions. For example:
1035
1036```roc
1037first_name : Str
1038first_name = "Amy"
1039
1040last_name : Str
1041last_name = "Lee"
1042```
1043
1044These annotations say that both `first_name` and `last_name` have the type `Str`.
1045
1046We can annotate records similarly. For example, we could move `first_name` and `last_name` into a record like so:
1047
1048```roc
1049amy : { first_name : Str, last_name : Str }
1050amy = { first_name: "Amy", last_name: "Lee" }
1051
1052jen : { first_name : Str, last_name : Str }
1053jen = { first_name: "Jen", last_name: "Majura" }
1054```
1055
1056### [Type Aliases](#type-aliases) {#type-aliases}
1057
1058When we have a recurring type annotation like this, it can be nice to give it its own name. We do this like so:
1059
1060```roc
1061Musician : { first_name : Str, last_name : Str }
1062
1063amy : Musician
1064amy = { first_name: "Amy", last_name: "Lee" }
1065
1066simone : Musician
1067simone = { first_name: "Simone", last_name: "Simons" }
1068```
1069
1070Here, `Musician` is a _type alias_. A type alias is like a def, except it gives a name to a type instead of to a value. Just like how you can read `name : Str` as "`name` has the type `Str`," you can also read `Musician : { first_name : Str, last_name : Str }` as "`Musician` has the type `{ first_name : Str, last_name : Str }`."
1071
1072### [Type Parameters](#type-parameters) {#type-parameters}
1073
1074Annotations for lists must specify what type the list's elements have:
1075
1076```roc
1077names : List Str
1078names = ["Amy", "Simone", "Tarja"]
1079```
1080
1081You can read `List Str` as "a list of strings." Here, `Str` is a _type parameter_ that tells us what type of `List` we're dealing with. `List` is a _parameterized type_, which means it's a type that requires a type parameter. There's no way to give something a type of `List` without a type parameter. You have to specify what type of list it is, such as `List Str` or `List Bool` or `List { first_name : Str, last_name : Str }`.
1082
1083### [Wildcard Types (\*)](#wildcard-type) {#wildcard-type}
1084
1085There are some functions that work on any list, regardless of its type parameter. For example, `List.is_empty` has this type:
1086
1087```roc
1088is_empty : List * -> Bool
1089```
1090
1091The `*` is a _wildcard type_; a type that's compatible with any other type. `List *` is compatible with any type of `List` like `List Str`, `List Bool`, and so on. So you can call `List.is_empty(["I am a List Str"])` as well as `List.is_empty([Bool.true])`, and they will both work fine.
1092
1093The 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 we can! This is because a `[]` value has the type `List *`. It is a "list with a wildcard type parameter", or a "list whose element type could be anything."
1094
1095### [Type Variables](#type-variables) {#type-variables}
1096
1097`List.reverse` works similarly to `List.is_empty`, but with an important distinction. As with `is_empty`, we can call `List.reverse` on any list, regardless of its type parameter. However, consider these calls:
1098
1099```roc
1100strings : List Str
1101strings = List.reverse(["a", "b"])
1102
1103bools : List Bool
1104bools = List.reverse([Bool.true, Bool.false])
1105```
1106
1107In 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`?
1108
1109We saw that `List.is_empty` 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.
1110
1111What we want is something like one of these:
1112
1113```roc
1114reverse : List elem -> List elem
1115```
1116
1117```roc
1118reverse : List value -> List value
1119```
1120
1121```roc
1122reverse : List a -> List a
1123```
1124
1125Any 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!
1126
1127You can choose any name you like for a type variable, but it has to be lowercase. (You may have 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.
1128
1129You 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.
1130
1131Similarly, 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).
1132
1133### [Tag Union Types](#tag-union-types) {#tag-union-types}
1134
1135We can also annotate types that include tags:
1136
1137```roc
1138color_from_str : Str -> [Red, Green, Yellow]
1139color_from_str = |string|
1140 when string is
1141 "red" -> Red
1142 "green" -> Green
1143 _ -> Yellow
1144```
1145
1146You can read the type `[Red, Green, Yellow]` as "a tag union of the tags `Red`, `Green`, and `Yellow`."
1147
1148Some tag unions have only one tag in them. For example:
1149
1150```roc
1151red_tag : [Red]
1152red_tag = Red
1153```
1154
1155### [Accumulating Tag Types](#accumulating-tag-types) {#accumulating-tag-types}
1156
1157Tag union types can accumulate more tags based on how they're used. Consider this `if` expression:
1158
1159```roc
1160|str|
1161 if Str.is_empty(str) then
1162 Ok "it was empty"
1163 else
1164 Err ["it was not empty"]
1165```
1166
1167Here, Roc sees that the first branch has the type `[Ok Str]` and that the `else` branch has the type `[Err (List Str)]`, so it concludes that the whole `if` expression evaluates to the combination of those two tag unions: `[Ok Str, Err (List Str)]`.
1168
1169This means this entire `|str| ...` function has the type `Str -> [Ok Str, Err (List Str)]`. However, it would be most common to annotate it as `Result Str (List Str)` instead, because the `Result` type (for operations like `Result.with_default`, which we saw earlier) is a type alias for a tag union with `Ok` and `Err` tags that each have one payload:
1170
1171```roc
1172Result ok err : [Ok ok, Err err]
1173```
1174
1175We just saw how tag unions get combined when different branches of a conditional return different tags. Another way tag unions can get combined is through pattern matching. For example:
1176
1177```roc
1178when color is
1179 Red -> "red"
1180 Yellow -> "yellow"
1181 Green -> "green"
1182```
1183
1184Here, Roc's compiler will infer that `color`'s type is `[Red, Yellow, Green]`, because those are the three possibilities this `when` handles.
1185
1186### [Opaque Types](#opaque-types) {#opaque-types}
1187
1188A type can be defined to be opaque to hide its internal structure. This is a lot more amazing than it may seem. It can make your code more modular, robust, and easier to read:
1189
1190- If a type is opaque you can modify its internal structure and be certain that no dependencies need to be updated.
1191- You can prevent that data needs to be checked multiple times. For example, you can create an opaque `NonEmptyList` from a `List` after you've checked it. Now all functions that you pass this `NonEmptyList` to do not need to handle the empty list case.
1192- Having the type `Username` in a type signature gives you more context compared to `Str`. Even if the `Username` is an opaque type for `Str`.
1193
1194You can create an opaque type with the `:=` operator. Let's make one called `Username`:
1195
1196```roc
1197Username := Str
1198
1199from_str : Str -> Username
1200from_str = |str|
1201 @Username(str)
1202
1203to_str : Username -> Str
1204to_str = |@Username(str)|
1205 str
1206```
1207
1208The `from_str` function turns a string into a `Username` by calling `@Username` on that string. The `to_str` function turns a `Username` back into a string by pattern matching `@Username(str)` to unwrap the string from the `Username` opaque type.
1209
1210Now we can expose the `Username` opaque type so that other modules can use it in type annotations. However, other modules can't use the `@Username` syntax to wrap or unwrap `Username` values. That operation is only available in the same scope where `Username` itself was defined; trying to use it outside that scope will give an error.
1211
1212Note that if we define `Username := Str` inside another module (e.g. `Main`) and also use `@Username`, this will compile, however the new `Username` type in main would not be equal to the one defined in the `Username` module. Although both opaque types have the name `Username`, they were defined in different modules and so they are type-incompatible with each other, and even attempting to use `==` to compare them would be a type mismatch.
1213
1214### [Integers](#integers) {#integers}
1215
1216Roc 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.
1217
1218Integer 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.
1219
1220For 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!
1221
1222The `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βΈ (=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).
1223
1224If 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βΈ different numbers. However, now in addition to one of those 256 numbers being zero, about half of the rest will be negative, and the others positive. So instead of ranging from, say -255 to 255 (which, counting zero, would represent 511 different numbers; too many to fit in 8 bits!) an `I8` value ranges from -128 to 127.
1225
1226Notice that the negative extreme is `-128` versus `127` (not `128`) on the positive side. That's because of needing room for zero; the slot for zero is taken from the positive range because zero doesn't have a minus sign.
1227
1228Following this pattern, the 16 in `I16` means that it's a signed 16-bit integer. That tells us it has room to represent 2ΒΉβΆ (=65536) different numbers. Half of 65536 is 32768, so the lowest `I16` would be -32768, and the highest would be 32767.
1229
1230Choosing a size depends on your performance needs and the range of numbers you want to represent. Consider:
1231
1232- 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!
1233- 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.
1234- 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!
1235
1236Here are the different fixed-size integer types that Roc supports:
1237
1238| Range | Type |
1239| ----------------------------------------------------------------------------------------------------------------- | ------ |
1240| `-128` <br> `127` | `I8` |
1241| `0` <br> `255` | `U8` |
1242| `-32_768` <br> `32_767` | `I16` |
1243| `0` <br> `65_535` | `U16` |
1244| `-2_147_483_648` <br> `2_147_483_647` | `I32` |
1245| `0` <br>`4_294_967_295` (over 4 billion) | `U32` |
1246| `-9_223_372_036_854_775_808` <br> `9_223_372_036_854_775_807` | `I64` |
1247| `0` <br>`18_446_744_073_709_551_615` (over 18 quintillion) | `U64` |
1248| `-170_141_183_460_469_231_731_687_303_715_884_105_728` <br> `170_141_183_460_469_231_731_687_303_715_884_105_727` | `I128` |
1249| `0` <br>`340_282_366_920_938_463_463_374_607_431_768_211_455` (over 340 undecillion) | `U128` |
1250
1251If any operation would result in an integer that is either too big or too small to fit in that range (e.g. calling `Int.max_i32 + 1`, which adds 1 to the highest possible 32-bit integer), then the operation will [overflow](https://en.wikipedia.org/wiki/Integer_overflow). When an overflow occurs, the program will crash.
1252
1253As such, it's very important to design your integer operations not to exceed these bounds!
1254
1255### [Fractions](#fractions) {#fractions}
1256
1257Roc has three fractional types:
1258
1259- `F32`, a 32-bit [floating-point number](https://en.wikipedia.org/wiki/IEEE_754)
1260- `F64`, a 64-bit [floating-point number](https://en.wikipedia.org/wiki/IEEE_754)
1261- `Dec`, a 128-bit decimal [fixed-point number](https://en.wikipedia.org/wiki/Fixed-point_arithmetic)
1262
1263These are different from integers, they can represent numbers with fractional components, such as 1.5 and -0.123.
1264
1265`Dec` is the best default choice for representing [base-10 decimal numbers](https://en.wikipedia.org/wiki/Decimal) like [currency](https://en.wikipedia.org/wiki/Currency), because it is base-10 under the hood. In contrast, `F64` and `F32` are [base-2](https://en.wikipedia.org/wiki/Binary_number) under the hood, which can lead to decimal precision loss even when doing addition and subtraction. For example, when using `F64`, running 0.1 + 0.2 returns 0.3000000000000000444089209850062616169452667236328125, whereas when using `Dec`, 0.1 + 0.2 returns 0.3.
1266
1267`F32` and `F64` have direct hardware support on common processors today. There is no hardware support for fixed-point decimals, so under the hood, a `Dec` is an `I128`; operations on it perform [base-10 fixed-point arithmetic](https://en.wikipedia.org/wiki/Fixed-point_arithmetic) with 18 decimal places of precision.
1268
1269This means a `Dec` can represent whole numbers up to slightly over 170 quintillion, along with 18 decimal places. (To be precise, it can store numbers between `-170_141_183_460_469_231_731.687303715884105728` and `170_141_183_460_469_231_731.687303715884105727`.) Why 18 decimal places? It's the highest number of decimal places where you can still convert any `U64` to a `Dec` without losing information.
1270
1271While 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).
1272
1273There 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.
1274
1275### [Num, Int, and Frac](#num-int-and-frac) {#num-int-and-frac}
1276
1277Some operations work on specific numeric types - such as `I64` or `Dec` - but some 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. Its type is:
1278
1279```roc
1280abs : Num a -> Num a
1281```
1282
1283This type says `abs` takes a number and then returns a number of the same type. Remember that we can see the type of number is the same because the [type variable](#type-variables) `a` is used on both sides. That's because the `Num` type is compatible with both integers and fractions.
1284
1285There's also an `Int` type which is only compatible with integers, and a `Frac` type which is only compatible with fractions. For example:
1286
1287```roc
1288Num.bitwise_xor : Int a, Int a -> Int a
1289```
1290
1291```roc
1292Num.cos : Frac a -> Frac a
1293```
1294
1295When you write a number literal in Roc, it has the type `Num *`. So you could call `Num.bitwise_xor(1, 1)` and also `Num.cos(1)` and have them all work as expected; the number literal `1` has the type `Num *`, which is compatible with the more constrained types `Int` and `Frac`. For the same reason, you can pass number literals to functions expecting even more constrained types, like `I32` or `F64`.
1296
1297### [Number Literals](#number-literals) {#number-literals}
1298
1299By default, a number literal with no decimal point has the type `Num *`βthat is, we know it's "a number" but nothing more specific. (Number literals with decimal points have the type `Frac *` instead.)
1300
1301You can give a number literal a more specific type by adding the type you want as a lowercase suffix. For example, `1u8` specifies `1` with the type `U8`, and `5dec` specifies `5` with the type `Dec`.
1302
1303The full list of possible suffixes includes:
1304
1305`u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`, `u128`, `i128`, `f32`, `f64`, `dec`
1306
1307Integer literals can be written in [hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) form by prefixing with `0x` followed by hexadecimal characters (`a` - `f` in addition to `0` - `9`). For example, writing `0xfe` is the same as writing `254`. Similarly, the prefix `0b` specifies binary integers. Writing `0b0000_1000` is the same as writing `8`.
1308
1309### [Default-Value Record Fields](#default-value-record-fields) {#default-value-record-fields}
1310
1311Sometimes you may want to write a function that accepts configuration options. The way to do this in Roc is to accept a record. When you do this, it can sometimes be convenient to provide default values for certain fields in that record, so the caller can omit those fields and let them be populated by the defaults you specified.
1312
1313For example:
1314
1315```roc
1316table = |{ height, width, title ?? "oak", description ?? "a wooden table" }| ...
1317```
1318
1319This is using _default value field destructuring_ to destructure a record while
1320providing default values for any fields that the caller didn't provide. Here, the `?` operator
1321essentially means "if the caller did not provide this field, default it to the following value."
1322
1323The type of `table` uses `??` instead of `:` to indicate which fields the caller can omit:
1324
1325```roc
1326table :
1327 {
1328 height : U64,
1329 width : U64,
1330 title ?? Str,
1331 description ?? Str,
1332 }
1333 -> Table
1334```
1335
1336This says that `table` takes a record with two fields that are _required_β`height` and
1337`width`βand two fields that may be _omitted_, namely `title` and `description`. It also says that
1338the `height` and `width` fields have the type `U64` and the `title` and `description` fields have
1339the type `Str`. This means you can choose to omit the `title`, `description`, or both fields,
1340when calling the functionβ¦but if you provide them, they must have the type `Str`.
1341
1342This is also the type that would have been inferred for `table` if it had no annotation.
1343Roc's compiler can tell from the destructuring syntax `title ?? "oak"` that `title` is a field with a default,
1344and that it has the type `Str`.
1345
1346Destructuring is the only way to implement a record with default value fields! For example,
1347if you write the expression `config.title` and `title` is a default value field, you'll get a compile error.
1348
1349Note that this language feature is really designed for passing configuration records to functions.
1350It's not intended to be used for data modeling; if you want to represent a value that might not be available,
1351use a tag union with tag names that describe why the value might not be there. For example, the tag union
1352`[Specified Str, Unspecified]` conveys different information from `[Found Str, NotFound]` even though both
1353of them store either a `Str` or nothing.
1354
1355## [Crashing](#crashing) {#crashing}
1356
1357Ideally, Roc programs would never crash. However, there are some situations where they may. For example:
1358
13591. When doing normal integer arithmetic (e.g. `x + y`) that [overflows](https://en.wikipedia.org/wiki/Integer_overflow).
13602. When the system runs out of memory.
13613. When a variable-length collection (like a `List` or `Str`) gets too long to be representable in the operating system's address space. (A 64-bit operating system's address space can represent several [exabytes](https://en.wikipedia.org/wiki/Byte#Multiple-byte_units) of data, so this case should not come up often.)
1362
1363Crashes in Roc are not like [try/catch exceptions](https://en.wikipedia.org/wiki/Exception_handling) found in some other programming languages. There is no way to "catch" a crash. It immediately ends the program, and what happens next is defined by the [platform](https://www.roc-lang.org/platforms). For example, a command-line interface platform might exit with a nonzero [exit code](https://en.wikipedia.org/wiki/Exit_status), whereas a web server platform might have the current request respond with a [HTTP 500 error](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#500).
1364
1365### [Crashing in unreachable branches](#crashing-in-unreachable-branches) {#crashing-in-unreachable-branches}
1366
1367You can intentionally crash a Roc program, for example inside a conditional branch that you believe is unreachable. Suppose you're certain that a particular `List U8` contains valid UTF-8 bytes, which means when you call `Str.from_utf8` on it, the `Result` it returns will always be `Ok`. In that scenario, you can use the `crash` keyword to handle the `Err` case like so:
1368
1369```roc
1370answer : Str
1371answer =
1372 when Str.from_utf8(definitely_valid_utf8) is
1373 Ok(str) -> str
1374 Err(_) -> crash "This should never happen!"
1375```
1376
1377If the unthinkable happens, and somehow the program reaches this `Err` branch even though that was thought to be impossible, then it will crash - just like if the system had run out of memory. The string passed to `crash` will be provided to the platform as context; each platform may do something different with it.
1378
1379> **Note:** `crash` is a language keyword and not a function; you can't assign `crash` to a variable or pass it to a function.
1380
1381### [Crashing for TODOs](#crashing-for-todos) {#crashing-for-todos}
1382
1383Another use for `crash` is as a TODO marker when you're in the middle of building something:
1384
1385```roc
1386if x > y then
1387 transmogrify(x * 2)
1388else
1389 crash "TODO handle the x <= y case"
1390```
1391
1392This lets you do things like write tests for the non-`crash` branch, and then come back and finish the other branch later.
1393
1394### [Crashing for error handling](#crashing-for-error-handling) {#crashing-for-error-handling}
1395
1396`crash` is not for error handling.
1397
1398The reason Roc has a `crash` keyword is for scenarios where it's expected that no error will ever happen (like in [unreachable branches](#crashing-in-unreachable-branches)), or where graceful error handling is infeasible (like running out of memory).
1399
1400Errors that are recoverable should be represented using normal Roc types (like [Result](https://www.roc-lang.org/builtins/Result)) and then handled without crashing. For example, by having the application report that something went wrong, and then continue running from there.
1401
1402## [Testing](#testing) {#testing}
1403
1404You can write automated tests for your Roc code like so:
1405
1406```roc
1407pluralize = |singular, plural, count|
1408 count_str = Num.to_str(count)
1409
1410 if count == 1 then
1411 "${count_str} ${singular}"
1412 else
1413 "${count_str} ${plural}"
1414
1415expect pluralize("cactus", "cacti", 1) == "1 cactus"
1416
1417expect pluralize("cactus", "cacti", 2) == "2 cacti"
1418```
1419
1420If you put this in a file named `main.roc` and run `roc test`, Roc will execute the two `expect` expressions (that is, the two `pluralize` calls) and report any that returned `Bool.false`.
1421
1422If a test fails, it will not show the actual value that differs from the expected value. This [will be resolved in the future](https://github.com/roc-lang/roc/issues/4633). For now, to show the actual value you can write the expect like this:
1423
1424```roc
1425expect
1426 func_out = pluralize("cactus", "cacti", 1)
1427
1428 func_out == "2 cactus"
1429```
1430
1431### [Inline Expectations](#inline-expects) {#inline-expects}
1432
1433Expects do not have to be at the top level:
1434
1435```roc
1436pluralize = |singular, plural, count|
1437 count_str = Num.to_str(count)
1438
1439 if count == 1 then
1440 "${count_str} ${singular}"
1441 else
1442 expect count > 0
1443
1444 "${count_str} ${plural}"
1445```
1446
1447This `expect` will fail if you call `pluralize` passing a count of 0.
1448
1449Note that inline `expect`s do not halt the program! They are designed to inform, not to affect control flow. Different `roc` commands will also handle `expect`s differently:
1450
1451- `roc build` discards all `expect`s for optimal runtime performance.
1452- `roc dev` only runs inline `expect`s that are encountered during normal execution of the program.
1453- `roc test` runs top level `expect`s and inline `expect`s that are encountered because of the running of top level `expect`s.
1454
1455Let's clear up any confusion with an example:
1456
1457```roc
1458main =
1459 expect 1 == 2
1460
1461 Stdout.line!("Hi there.")
1462
1463double = |num|
1464 expect num > -1
1465
1466 num * 2
1467
1468expect double(0) == 0
1469```
1470
1471- `roc build` will run `main`, ignore `expect 1 == 2` and just print `Hi there.`.
1472- `roc dev` will run `main`, tell you `expect 1 == 2` failed but will still print `Hi there`.
1473- `roc test` will run `expect double(0) == 0` followed by `expect num > -1` and will print how many top level expects passed: `0 failed and 1 passed in 100 ms.`.
1474
1475## [Modules](#modules) {#modules}
1476
1477Each `.roc` file is a separate module and contains Roc code for different purposes. Here are the types of modules:
1478
1479- app [(example)](https://github.com/roc-lang/examples/blob/main/examples/HelloWorld/main.roc): Applications are combined with a platform and compiled into an executable.
1480- module [(example)](https://github.com/roc-lang/examples/blob/main/examples/MultipleRocFiles/Hello.roc): Provide types and functions which can be imported into other modules.
1481- package [(example)](https://github.com/lukewilliamboswell/roc-json/blob/main/package/main.roc): Organises modules to share functionality across applications and platforms.
1482- platform [(example)](https://github.com/roc-lang/basic-cli/blob/main/platform/main.roc): Provides memory management and effects like writing to files, network communication,... to interface with the outside world. [Detailed explanation](https://www.roc-lang.org/platforms).
1483- hosted [(example)](https://github.com/roc-lang/basic-cli/blob/main/platform/Host.roc): Lists all Roc types and functions provided by the platform.
1484
1485### [Builtin Modules](#builtin-modules) {#builtin-modules}
1486
1487There are several modules that are built into the Roc compiler, which are imported automatically into every Roc module. They are:
1488
14891. [Str](https://www.roc-lang.org/builtins/Str)
14902. [Num](https://www.roc-lang.org/builtins/Num)
14913. [Bool](https://www.roc-lang.org/builtins/Bool)
14924. [Result](https://www.roc-lang.org/builtins/Result)
14935. [List](https://www.roc-lang.org/builtins/List)
14946. [Dict](https://www.roc-lang.org/builtins/Dict)
14957. [Set](https://www.roc-lang.org/builtins/Set)
14968. [Decode](https://www.roc-lang.org/builtins/Decode)
14979. [Encode](https://www.roc-lang.org/builtins/Encode)
149810. [Hash](https://www.roc-lang.org/builtins/Hash)
149911. [Box](https://www.roc-lang.org/builtins/Box)
150012. [Inspect](https://www.roc-lang.org/builtins/Inspect)
1501
1502You may have noticed that we already used the first five. For example, when we wrote `Str.concat` and `Num.is_even`, we were referencing functions stored in the `Str` and `Num` modules.
1503
1504These 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!"
1505
1506Besides being built into the compiler, the builtin modules are different from other modules in that:
1507
1508- They are always imported. You never need to add them to `imports`.
1509- All their types are imported unqualified automatically. So you never need to write `Num.Dec`, because it's as if the `Num` module was imported using `imports [Num.{ Dec }]` (the same is true for all the other types in the `Num` module.)
1510
1511### [App Module Header](#app-module-header) {#app-module-header}
1512
1513Let's take a closer look at the part of `main.roc` above the `main` def:
1514
1515```roc
1516app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br" }
1517
1518import pf.Stdout
1519```
1520
1521This 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_ because it begins with the `app` keyword.
1522
1523The line `app [main!]` shows that this module is a Roc application and which [platform](https://www.roc-lang.org/platforms) it is built on.
1524
1525The `{ pf: platform "https://...tar.br" }` part says four things:
1526
1527- We're going to be using a _package_ (a collection of modules) that can be downloaded from the URL `"https://...tar.br"`
1528- That package's [base64](https://en.wikipedia.org/wiki/Base64#URL_applications)\-encoded [BLAKE3](<https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE3>) cryptographic hash is the long string at the end (before the `.tar.br` file extension). Once the file has been downloaded, its contents will be verified against this hash, and it will only be installed if they match. This way, you can be confident the download was neither corrupted nor changed since it was originally published.
1529- We're going to name that package `pf` so we can refer to it more concisely in the future.
1530- This package is the [platform](#platform-modules-platform-modules) we have chosen for our app.
1531
1532The `import pf.Stdout` line says that we want to import the `Stdout` module from the `pf` package, and make it available in the current module.
1533
1534This import has a direct interaction with our definition of `main`. Let's look at that again:
1535
1536```roc
1537main! = |_args|
1538 Stdout.line!("Hi there, from inside a Roc app. π")
1539```
1540
1541Here, `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`.
1542
1543When we write `import pf.Stdout`, it specifies that the `Stdout` module comes from the package we named `pf` in the `packages { pf: ... }` section.
1544
1545You can find documentation for the `Stdout.line!` function in the [Stdout](https://roc-lang.github.io/basic-cli/0.19.0/Stdout/#line!) module documentation.
1546
1547If we would like to include other modules in our application, say `AdditionalModule.roc` and `AnotherModule.roc`, then they can be imported directly like this:
1548
1549```roc
1550import pf.Stdout
1551import AdditionalModule
1552import AnotherModule
1553```
1554
1555You can also use the `as` keyword if you would like to use a different name:
1556
1557```roc
1558import uuid.Generate as Uuid
1559```
1560
1561...and the `exposing` keyword to bring values or functions into the current scope:
1562
1563```roc
1564import pf.Stdout exposing [line!]
1565
1566main! = |_args|
1567 line!("Hi there, from inside a Roc app. π")
1568```
1569
1570### [Package Modules](#package-modules) {#package-modules}
1571
1572Package modules enable Roc code to be easily re-used and shared. This is achieved by organizing code into different modules and then including these in the `package` field of the package file structure, `package [ MyModule ] {}`. The modules that are listed in the `package` field are then available for use in applications, platforms, or other packages. Internal modules that are not listed will be unavailable for use outside of the package.
1573
1574See [Parser Package](https://github.com/lukewilliamboswell/roc-parser/tree/main/package) for an example.
1575
1576Package documentation can be generated using the Roc cli with `roc docs /package/*.roc`.
1577
1578Build a package for distribution with `roc build --bundle .tar.br /package/main.roc`. This will create a single tarball that can then be easily shared online using a URL.
1579
1580You can import a package that is available either locally, or from a URL into a Roc application or platform. This is achieved by specifying the package in the `packages` section of the application or platform file structure. For example, `{ .., parser: "<package URL>" }` is an example that imports a parser module from a URL.
1581
1582How does the Roc cli import and download a package from a URL?
1583
15841. First it checks to see whether the relevant folder already exists in the local filesystem and if not, creates it. If there is a package already downloaded then there is no need to download or extract anything. Packages are cached in a directory, typically `~/.cache/roc` on UNIX, and `%APPDATA%\\Roc` on Windows.
15852. It then downloads the file at that URL and verifies that the hash of the file matches the hash at the end of the URL.
15863. If the hash of the file matches the hash in the URL, then decompress and extract its contents into the cache folder so that it can be used.
1587
1588Why is a Roc package URL so long?
1589
1590Including the hash solves a number of problems:
1591
15921. The package at the URL can not suddenly change and cause different behavior.
15932. Because of 1. there is no need to check the URL on every compilation to see if we have the latest version.
15943. If the domain of the URL expires, a malicious actor can change the package but the hash will not match so the roc cli will reject it.
1595
1596### [Regular Modules](#regular-modules) {#regular-modules}
1597
1598\[This part of the tutorial has not been written yet. Coming soon!\]
1599
1600### [Platform Modules](#platform-modules) {#platform-modules}
1601
1602\[This part of the tutorial has not been written yet. Coming soon!\]
1603
1604### [Importing Files](#importing-files) {#importing-files}
1605
1606You can import files directly into your module as a `Str` or a `List U8` at compile time. This is can be useful when working with data you would like to keep in a separate file, e.g. JSON or YAML configuration.
1607
1608```roc
1609import "some-file" as some_str : Str
1610import "some-file" as some_bytes : List U8
1611```
1612
1613See the [Ingest Files Example](https://www.roc-lang.org/examples/IngestFiles/README.html) for a demonstration on using this feature.
1614
1615## [Effectful functions](#efffectful-functions) {#efffectful-functions}
1616
1617There are two types of functions in roc, "pure" and "effectful". Consider these two functions:
1618
1619```roc
1620with_extension : Str -> Str
1621with_extension = |filename|
1622 "${filename}.roc"
1623
1624read_file! : Str => Str
1625read_file! = |path|
1626 File.read_utf8!(with_extension(path)) |> Result.with_default("")
1627```
1628
1629Notice the subtle difference in these functions' types:
1630
1631- Str `->` Str is the type of the pure function `with_extension`
1632- Str `=>` Str is the type of the effectful function `read_file!`
1633
1634The arrow in the type tells you whether a function is pure or might perform effects. Pure functions use `->` and effectful functions use `=>`. Effectful functions can call either pure functions or other effectful functions, but pure functions can only call other pure functions. (The fact that pure functions can only call pure functions is part of the definition of pure functions; Roc is just reflecting that fact in the type system.)
1635
1636The suffix character `!` at the end of a function name marks it as effectful. This is a naming convention, and is enforced by the compiler with a warning.
1637
1638Let's look at some examples. In the `basic-cli` platform, here are four operations we can do:
1639
1640- Write a string to the terminal
1641- Read a string from user input
1642- Write a string to a file
1643- Read a string from a file
1644
1645We'll use these four operations to learn about effects.
1646
1647Let's revisit that first application.
1648
1649```roc
1650app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br" }
1651
1652import pf.Stdout
1653
1654main! = |_args|
1655 Stdout.line!("Hi there, from inside a Roc app. π")
1656```
1657
1658This code prints "Hi there, from inside a Roc app. π" to the [standard output](<https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)>). `Stdout.line` has this type:
1659
1660```roc
1661Stdout.line! : Str => Result {} [StdoutErr IOErr]
1662```
1663
1664An effectful function is capable of interacting with state outside your Roc program, such as the terminal's standard output, or a file.
1665
1666When we call `main!`, the host will provide the arguments passed from the cli `_args` (here we're just ignoring these), and then run. Here, we've set `main!` to be an effectful function that writes `"Hi there, from inside a Roc app. π"` to `stdout` when it gets run, so that's what our program does!
1667
1668The `Stdout.line!` function here returns a `Result` when called. If it succeeds it returns the unit value `{}`, or if it fails it returns the tag `StdoutErr` with an `IOErr` payload.
1669
1670In contrast, when `Stdin.line! : {} => Result Str [EndOfFile, StdinErr IOErr]` finishes reading a line from [standard input](<https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)>), it produces either a `Str` or else `EndOfFile` or a `StdinErr` error tag if standard input reached its end (which can happen if the user types Ctrl+D on UNIX systems or Ctrl+Z on Windows). These possibilities can all be seen just in the type definition of the function.
1671
1672Note that to call this `Stdin.line!`, you need to provide the unit value `{}` as an argument.
1673
1674### [Reading values](#reading-values) {#reading-values}
1675
1676Let's change `main!` to read a line from `stdin`, and then print what we got:
1677
1678```roc
1679app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br" }
1680
1681import pf.Stdout
1682import pf.Stdin
1683
1684main! = |_args|
1685 Stdout.line!("Type in something and press Enter:")?
1686 input = Stdin.line!({})?
1687 Stdout.line!("Your input was: ${input}")
1688```
1689
1690If you run this program, it will print "Type in something and press Enter:" and then pause.
1691That's because it's waiting for you to type something in and press Enter! Once you do,
1692it should print back out what you entered.
1693
1694### [Effectful failure](#failure) {#failure}
1695
1696Sometimes, effects can fail. For example, reading from a file might fail if the file is not found.
1697Even reading from stdin and writing to stdout can fail!
1698
1699For example, the `Stdin.line!` function can fail if stdin is closed before it receives a line. You can try this out
1700by running the program and pressing Ctrl+Z on Windows, or Ctrl+D on macOS or Linux. (Press that key rather than
1701typing in text and pressing Enter.) You'll see a default error message, which the `basic-cli` platform provides
1702in case an error occurs that we didn't handle.
1703
1704Although this default error handling behavior might be exactly what we want if we're writing a quick script,
1705high-quality programs handle errors gracefully. Fortunately, we can do this nicely in Roc!
1706
1707If we wanted to add the type annotation to `main!` that Roc is inferring for it, we would add this annotation:
1708
1709```roc
1710import pf.Stdout
1711import pf.Stdin
1712import pf.Arg exposing [Arg]
1713
1714main! : List Arg => Result {} [EndOfFile, StdinErr Stdin.IOErr, StdoutErr Stdout.IOErr]
1715main = #...
1716```
1717
1718Let's break down what this type is saying:
1719
1720- Both the `!` suffix in the name, and the `=>` in the type, tell us this is an effectful function. Its two type parameters are just like the ones we saw in `Result` earlier: the first type tells us what this function will produce if it succeeds, and the other one tells us what it will produce if it fails.
1721- `{}` tells us that this function always produces an empty record when it succeeds. (That is, it doesn't produce anything useful. Empty records don't have any information in them!) This is because the last expression in `main!` comes from `Stdout.line!`, which doesn't produce anything. (In contrast, the `Stdin` function's first type parameter is a `Str`, because it produces a `Str` if it succeeds.)
1722- `[EndOfFile, StdinErr Stdin.IOErr, StdoutErr Stdout.IOErr]` tells us the different ways this function can fail. The `StdoutErr` and `StdinErr` tags are there because we used `Stdout.line!` and `Stdin.line!`.
1723
1724To understand the error a little more, let's try temporarily commenting out our current `main!` and replacing
1725it with this one:
1726
1727```roc
1728main! : _ => Result {} [Exit I32 Str]
1729main! = |_args| Err(Exit(42, "An error happened!"))
1730```
1731
1732Now if we run the application, it will print the line "An error happened!" to stderr and exit with a status code of 42. (You can check the status code of the most recent terminal command that finished in Windows by running `echo %ERRORLEVEL%` (or `$LASTEXITCODE` in PowerShell), or by running `echo $?` in macOS or Linux.)
1733
1734Now let's try running it with this version of `main`:
1735
1736```roc
1737main! : _ => Result {} [Exit I32 Str]
1738main! = |_args| Ok({})
1739```
1740
1741This program won't print anything at all, but it will exit with a status code of `0`, indicating success.
1742
1743In summary:
1744
1745- If the `main!` function ends in a `Ok({})`, then it means the final expression succeeded and the program will exit with status code 0.
1746- If the `main!` function ends in a `Err(Exit(42, "β¦"))`, then it means it failed, and the only information we got about the failure was that the program should exit with code 42 instead of 0, and that it should print a particular string to stderr to inform the user about what happened.
1747
1748### [Handling failure](#handling-failure) {#handling-failure}
1749
1750If `main!` ends up failing with any other errors besides `Exit` (such as `StdoutErr` or `StdinErr`), then the `basic-cli` platform's automatic error handling will handle them by printing out words taken from the source code (such as "StdoutErr" and "StdinErr"), which could lead to a bad experience for people using this program!
1751
1752We can prevent that by gracefully handling the other error types, and then translating them into `Exit` errors so that they affect the program's exit code and don't result in the platform printing anything.
1753
1754A convenient way to make sure we've handled all the other errors is to keep our current type annotation for `main!` but restore our old implementation:
1755
1756```roc
1757main! : List Arg => Result {} [Exit I32 Str]
1758main! = |_args|
1759 Stdout.line!("Type in something and press Enter:")?
1760 input = Stdin.line!({})?
1761 Stdout.line!("Your input was: ${input}")
1762```
1763
1764Adding this type annotation will give us a type mismatch - which is exactly what we want in this case!
1765
1766The type mismatch is telling us that we're claiming the `main!` function will only ever fail with an `Exit` tag, but this implementation can _also_ fail with `EndOfFile`, `StdoutErr` and `StdinErr` tags we saw earlier.
1767
1768In other words, adding this annotation effectively opted us out of `basic-cli`'s default error handling. Now any potential failures (now and in the future) will have to be handled somehow; if we forget to handle any, we'll get a type mismatch like this! For that reason, `basic-cli` applications that are intended to be high-quality (so, not things like quick scripts) will generally benefit from applying this type annotation to `main!`.
1769
1770Here's one way we can handle those errors:
1771
1772```roc
1773app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br" }
1774
1775import pf.Stdout
1776import pf.Stdin
1777import pf.Arg exposing [Arg]
1778
1779main! : List Arg => Result {} [Exit I32 Str]
1780main! = |_args|
1781 Result.map_err(my_function!({}), |err|
1782 when err is
1783 StdoutErr(_) -> Exit(1i32, "Error writing to stdout.")
1784 StdinErr(_) -> Exit(2i32, "Error writing to stdin.")
1785 EndOfFile -> Exit(3i32, "End of file reached.")
1786 )
1787
1788my_function! : {} => Result {} [EndOfFile, StdinErr _, StdoutErr _]
1789my_function! = |{}|
1790 Stdout.line!("Type in something and press Enter:")?
1791 input = Stdin.line!({})?
1792 Stdout.line!("Your input was: ${input}")
1793```
1794
1795The `Result.map_err` function translates one error into another. Here, we're translating the `EndOfFile`, `StdoutErr` and `StdinErr` errors into `Exit` errors which include a different exit code plus a message that will print to stderr to explain what happened.
1796
1797### [The \_ type](#underscore) {#underscore}
1798
1799Note from our last example how we used `_` to create _partial_ type annotations. For example
1800
1801```roc
1802when err is
1803 StdoutErr(_) -> Exit(1i32, "Error writing to stdout.")
1804 StdinErr(_) -> Exit(2i32, "Error writing to stdin.")
1805 EndOfFile -> Exit(3i32, "End of file reached.")
1806```
1807
1808Wherever a `_` appears in a type annotation, it essentially means "I'm choosing not to annotate this part right here" and it lets Roc use type inference to fill in the blank behind the scenes. Roc will still compile them and check their types as normal (just like it did before we had any annotations at all); the `_` is about which parts of the type we're choosing to annotate and which parts we're leaving to inference.
1809
1810This is a useful technique to use when we don't want to write out a bunch of error types that we're going to handle anyway, and would otherwise have to keep updating every time a new error appeared. If we want to know the full list of errors, we can see it in a number of ways:
1811
1812- Look at the branches of our `when err is`, since they cover all the errors (and we'd get a compile error if we left any out)
1813- If we're using an editor that supports it, hovering over the `_` might display the inferred type that goes there.
1814- We can put an obviously wrong type in there (e.g. replace the `{}` with `Str`, which is totally wrong) and look at the compiler error to see what it inferred as the correct type.
1815
1816### [Ignoring informationless return values](#ignoring-informationless-return-values) {#ignoring-informationless-return-values}
1817
1818The compiler warns us about any unused variable, but as we saw before, if we don't intend to use a variable then we can name it `_` to clarify intent and silence the warning.
1819
1820Furthermore, if a variable can't possibly contain any useful information, then we can ignore it entirely.
1821
1822Let's revisit an earlier example:
1823
1824```roc
1825app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br" }
1826
1827import pf.Stdout
1828import pf.Stdin
1829
1830main! = |_args|
1831 Stdout.line!("Type in something and press Enter:")?
1832 input = Stdin.line!({})?
1833 Stdout.line!("Your input was: ${input}")
1834```
1835
1836It looks like this block goes "expression, assignment, expression", but expressions are only allowed on the last line of a block. What's happening here?
1837
1838Since `Stdout.line! : Str => Result {} [StdoutErr IOErr]`, in the above we are unwrapping the `Result` returned by calling these effects.
1839
1840An alternative option, is to just ignore the return value entirely:
1841
1842```roc
1843main! = |_args|
1844 _ = Stdout.line!("Type in something and press Enter:")?
1845 when Stdin.line!({}) is
1846 Ok(input) ->
1847 _ = Stdout.line!("Your input was: ${input}")?
1848 Ok({})
1849 Err(_) ->
1850 Ok({})
1851```
1852
1853### [Tagging errors](#tagging-errors) {#tagging-errors}
1854
1855Although it's rare, it is possible that either of the `Stdout.line!` operations in our example could fail:
1856
1857```roc
1858main! = |_args|
1859 Stdout.line!("Type something and press Enter.")?
1860 input = Stdin.line!({})?
1861 Stdout.line!("You entered: ${input}")
1862```
1863
1864(In this particular example, it's very unlikely that this would come up at all, and even if it did, we might not care which one caused the problem. But you can imagine having multiple HTTP requests, or file writes, and wanting to know which of them was the one that failed.)
1865
1866If an error happened here, we wouldn't know which effectful function was the cause of the failure.
1867
1868One option is to "tag the error" using `Result.map_err` to wrap the error in a [tag](#tags) like so:
1869
1870```roc
1871app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br" }
1872
1873import pf.Stdout
1874import pf.Stdin
1875
1876main! = |_args|
1877
1878 Stdout.line!("Type something and press Enter.")
1879 |> Result.map_err(UnableToPrintPrompt)?
1880
1881 input =
1882 Stdin.line!({})
1883 |> Result.map_err(UnableToReadInput)?
1884
1885 Stdout.line!("You entered: ${input}")
1886 |> Result.map_err(UnableToPrintInput)?
1887
1888 Ok({})
1889```
1890
1891The `map_err` function has this type:
1892
1893```
1894Result.map_err : Result ok a, (a -> b) -> Result ok b
1895```
1896
1897Here we're passing in "tagging functions" β namely, `UnableToPrintPrompt` and `UnableToReadInput`. (See [Using tags as functions](#using-tags-as-functions) for how this works.)
1898
1899This code is doing three things:
1900
19011. Call `Stdout.line!("...")`, which returns a `Result` value
19022. Transform that `Result` value into another `Result` value using `|> Result.map_err`
19033. Unwrap that final `Result` value (returned by `map_err`) using `try` and return early if it's an error
1904
1905#### Another option - the ? _infix_ operator
1906
1907The above example could be rewritten to the following with no change to how it
1908would behave:
1909
1910```roc
1911app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.20.0/X73hGh05nNTkDHU06FHC0YfFaQB1pimX7gncRcao5mU.tar.br" }
1912
1913import pf.Stdout
1914import pf.Stdin
1915
1916main! = |_args|
1917 Stdout.line!("Type something and press Enter.") ? UnableToPrintPrompt
1918 input = Stdin.line!({}) ? UnableToReadInput
1919 Stdout.line!("You entered: ${input}") ? UnableToPrintInput
1920 Ok({})
1921```
1922
1923Here the `?` is just syntax sugar that does the same thing as the `?` postfix
1924operator, put passes any error that would be returned to the function (or in
1925this case tag) before it is returned.
1926
1927See the [Error Handling example](https://www.roc-lang.org/examples/ErrorHandlingRealWorld/README) for a more detailed explanation of error handling in a larger program.
1928
1929### [Displaying Roc values with `Inspect.to_str`](#inspect) {#inspect}
1930
1931The [`Inspect.to_str`](https://www.roc-lang.org/builtins/Inspect#to_str) function returns a `Str` representation of any Roc value using its [`Inspect` ability](/abilities#inspect-ability). It's useful for things like debugging and logging (although [`dbg`](https://www.roc-lang.org/tutorial#debugging) is often nicer for debugging in particular), but its output is almost never something that should be shown to end users! In this case we're just using it for our own learning, but it would be better to run a `when` on `e` and display a more helpful message.
1932
1933```roc
1934when err is
1935 StdoutErr(e) -> Exit 1 "Error writing to stdout: ${Inspect.to_str(e)}"
1936 StdinErr(e) -> Exit 2 "Error writing to stdin: ${Inspect.to_str(e)}"
1937```
1938
1939### [The early `return` keyword](#the-early-return-keyword) {#the-early-return-keyword}
1940
1941The `return` keyword can interrupt a function and return any value we want.
1942
1943This is rarely necessary, due to Roc's powerful syntax for expressions and pattern matching, but it's included as a comfort to developers familiar with its frequent use in primarily-imperative programing languages.
1944
1945For example:
1946
1947```roc
1948stoplight_str : Str
1949stoplight_str =
1950 stoplight_color =
1951 if this_is_a_bad_time then
1952 return "Hey, listen, I just don't want to do this."
1953 else
1954 previous_stop_light_color
1955 when stoplight_color is
1956 Red -> "red"
1957 Green | Yellow if contrast > 75 -> "not red, but very high contrast"
1958 Green | Yellow if contrast > 50 -> "not red, but high contrast"
1959 Green | Yellow -> "not red"
1960```
1961
1962## [Examples](#examples) {#examples}
1963
1964Well done on making it this far!
1965
1966We've covered all of the basic syntax and features of Roc in this Tutorial. You should now have a good foundation and be ready to start writing your own applications.
1967
1968You can continue reading through more-advanced topics below, or perhaps checkout some of the [Examples](/examples) for more a detailed exploration of ways to do various things.
1969
1970## [Advanced Concepts](#advanced-concepts) {#advanced-concepts}
1971
1972Here are some concepts you likely won't need as a beginner, but may want to know about eventually. This is listed as an appendix rather than the main tutorial, to emphasize that it's totally fine to stop reading here and go build things!
1973
1974### [Open Records and Closed Records](#open-records-and-closed-records) {#open-records-and-closed-records}
1975
1976Let's say I write a function which takes a record with a `first_name` and `last_name` field, and puts them together with a space in between:
1977
1978```roc
1979full_name = |user|
1980 "${user.first_name} ${user.last_name}"
1981```
1982
1983I can pass this function a record that has more fields than just `first_name` and `last_name`, as long as it has _at least_ both of those fields (and both of them are strings). So any of these calls would work:
1984
1985- `full_name({ first_name: "Sam", last_name: "Sample" })`
1986- `full_name({ first_name: "Sam", last_name: "Sample", email: "blah@example.com" })`
1987- `full_name({ age: 5, first_name: "Sam", things: 3, last_name: "Sample", role: Admin })`
1988
1989This `user` argument is an _open record_ - that is, a description of a minimum set of fields on a record, and their types. When a function takes an open record as an argument, it's okay if you pass it a record with more fields than just the ones specified.
1990
1991In contrast, a _closed record_ is one that requires an exact set of fields (and their types), with no additional fields accepted.
1992
1993If we add a type annotation to this `full_name` function, we can choose to have it accept either an open record or a closed record:
1994
1995```roc
1996# Closed record
1997full_name : { first_name : Str, last_name : Str } -> Str
1998full_name = |user|
1999 "${user.first_name} ${user.last_name}"
2000```
2001
2002```roc
2003# Open record (because of the `*`)
2004full_name : { first_name : Str, last_name : Str }* -> Str
2005full_name = |user|
2006 "${user.first_name} ${user.last_name}"
2007```
2008
2009The `*` in the type `{ first_name : Str, last_name : Str }*` is what makes it an open record type. This `*` is the _wildcard type_ we saw earlier with empty lists. (An empty list has the type `List *`, in contrast to something like `List Str` which is a list of strings.)
2010
2011This is because record types can optionally end in a type variable. Just like how we can have `List *` or `List a -> List a`, we can also have `{ first : Str, last : Str }*` or `{ first : Str, last : Str }a -> { first : Str, last : Str }a`. The differences are that in `List a`, the type variable is required and appears with a space after `List`; in a record, the type variable is optional, and appears (with no space) immediately after `}`.
2012
2013If the type variable in a record type is a `*` (such as in `{ first : Str, last : Str }*`), then it's an open record. If the type variable is missing, then it's a closed record. You can also specify a closed record by putting a `{}` as the type variable (so for example, `{ email : Str }{}` is another way to write `{ email : Str }`). In practice, closed records are basically always written without the `{}` on the end, but later on we'll see a situation where putting types other than `*` in that spot can be useful.
2014
2015### [Constrained Records](#constrained-records) {#constrained-records}
2016
2017The type variable can also be a named type variable, like so:
2018
2019```roc
2020add_https : { url : Str }a -> { url : Str }a
2021add_https = |record|
2022 { record & url: "https://${record.url}" }
2023```
2024
2025This function uses _constrained records_ in its type. The annotation is saying:
2026
2027- This function takes a record which has at least a `url` field, and possibly others
2028- That `url` field has the type `Str`
2029- It returns a record of exactly the same type as the one it was given
2030
2031So if we give this function a record with five fields, it will return a record with those same five fields. The only requirement is that one of those fields must be `url: Str`.
2032
2033In practice, constrained records appear in type annotations much less often than open or closed records do.
2034
2035Here's when you can typically expect to encounter these three flavors of type variables in records:
2036
2037- _Open records_ are what the compiler infers when you use a record as an argument, or when destructuring it (for example, `{ x, y } =`).
2038- _Closed records_ are what the compiler infers when you create a new record (for example, `{ x: 5, y: 6 }`)
2039- _Constrained records_ are what the compiler infers when you do a record update (for example, `{ user & email: new_email }`)
2040
2041Of note, you can pass a closed record to a function that accepts a smaller open record, but not the reverse. So a function `{ a : Str, b : Bool }* -> Str` can accept an `{ a : Str, b : Bool, c : Bool }` record, but a function `{ a : Str, b : Bool, c : Bool } -> Str` would not accept an `{ a : Str, b : Bool }*` record.
2042
2043This is because if a function accepts `{ a : Str, b : Bool, c : Bool }`, that means it might access the `c` field of that record. So if you passed it a record that was not guaranteed to have all three of those fields present (such as an `{ a : Str, b : Bool }*` record, which only guarantees that the fields `a` and `b` are present), the function might try to access a `c` field at runtime that did not exist!
2044
2045### [Type Variables in Record Annotations](#type-variables-in-record-annotations) {#type-variables-in-record-annotations}
2046
2047You can add type annotations to make record types less flexible than what the compiler infers, but not more flexible. For example, you can use an annotation to tell the compiler to treat a record as closed when it would be inferred as open (or constrained), but you can't use an annotation to make a record open when it would be inferred as closed.
2048
2049If you like, you can always annotate your functions as accepting open records. However, in practice this may not always be the nicest choice. For example, let's say you have a `User` type alias, like so:
2050
2051```roc
2052User : {
2053 email : Str,
2054 first_name : Str,
2055 last_name : Str,
2056}
2057```
2058
2059This defines `User` to be a closed record, which in practice is the most common way records named `User` tend to be defined.
2060
2061If you want to have a function take a `User`, you might write its type like so:
2062
2063```roc
2064is_valid : User -> Bool
2065```
2066
2067If you want to have a function return a `User`, you might write its type like so:
2068
2069```roc
2070user_from_email : Str -> User
2071```
2072
2073A function which takes a user and returns a user might look like this:
2074
2075```roc
2076capitalize_names : User -> User
2077```
2078
2079This is a perfectly reasonable way to write all of these functions. However, I might decide that I really want the `is_valid` function to take an open record; a record with _at least_ the fields of this `User` record, but possibly others as well.
2080
2081Since open records have a type variable (like `*` in `{ email : Str }*` or `a` in `{ email : Str }a -> { email : Str }a`), in order to do this I'd need to add a type variable to the `User` type alias:
2082
2083```roc
2084User a : {
2085 email : Str,
2086 first_name : Str,
2087 last_name : Str
2088}a
2089```
2090
2091Notice that the `a` type variable appears not only in `User a` but also in `}a` at the end of the record type!
2092
2093Using `User a` type alias, I can still write the same three functions, but now their types need to look different. This is what the first one would look like:
2094
2095```roc
2096is_valid : User * -> Bool
2097```
2098
2099Here, the `User *` type alias substitutes `*` for the type variable `a` in the type alias, which takes it from `{ email : Str, ... }a` to `{ email : Str, ... }*`. Now I can pass it any record that has at least the fields in `User`, and possibly others as well, which was my goal.
2100
2101```roc
2102user_from_email : Str -> User {}
2103```
2104
2105Here, the `User {}` type alias substitutes `{}` for the type variable `a` in the type alias, which takes it from `{ email : Str, ... }a` to `{ email : Str, ... }{}`. As noted earlier, this is another way to specify a closed record: putting a `{}` after it, in the same place that you'd find a `*` in an open record.
2106
2107> **Aside:** This works because you can form new record types by replacing the type variable with other record types. For example, `{ a : Str, b : Str }` can also be written `{ a : Str }{ b : Str }`. You can chain these more than once, e.g. `{ a : Str }{ b : Str }{ c : Str, d : Str }`. This is more useful when used with type annotations; for example, `{ a : Str, b : Str }User` describes a closed record consisting of all the fields in the closed record `User`, plus `a : Str` and `b : Str`.
2108
2109This function still returns the same record as it always did, it just needs to be annotated as `User {}` now instead of just `User`, because the `User` type alias has a variable in it that must be specified.
2110
2111The third function might need to use a named type variable:
2112
2113```roc
2114capitalize_names : User a -> User a
2115```
2116
2117If this function does a record update on the given user, and returns that - for example, if its definition were `capitalize_names = |user| { user & email: "blah" }` - then it needs to use the same named type variable for both the argument and return value.
2118
2119However, if returns a new `User` that it created from scratch, then its type could instead be:
2120
2121```roc
2122capitalize_names : User * -> User {}
2123```
2124
2125This says that it takes a record with at least the fields specified in the `User` type alias, and possibly others...and then returns a record with exactly the fields specified in the `User` type alias, and no others.
2126
2127These three examples illustrate why it's relatively uncommon to use open records for type aliases: it makes a lot of types need to incorporate a type variable that otherwise they could omit, all so that `is_valid` can be given something that has not only the fields `User` has, but some others as well. (In the case of a `User` record in particular, it may be that the extra fields were included due to a mistake rather than on purpose, and accepting an open record could prevent the compiler from raising an error that would have revealed the mistake.)
2128
2129That said, this is a useful technique to know about if you want to (for example) make a record type that accumulates more and more fields as it progresses through a series of operations.
2130
2131### [Open and Closed Tag Unions](#open-and-closed-tag-unions) {#open-and-closed-tag-unions}
2132
2133Just like how Roc has open records and closed records, it also has open and closed tag unions.
2134
2135The _open tag union_ (or _open union_ for short) `[Foo Str, Bar Bool]*` represents a tag that might be `Foo Str` and might be `Bar Bool`, but might also be some other tag whose type isn't known at compile time.
2136
2137Because an open union represents possibilities that are impossible to know ahead of time, any `when` I use on a `[Foo Str, Bar Bool]*` value must include a catch-all `_ ->` branch. Otherwise, if one of those unknown tags were to come up, the `when` would not know what to do with it! For example:
2138
2139```roc
2140example : [Foo Str, Bar Bool]* -> Bool
2141example = |tag|
2142 when tag is
2143 Foo(str) -> Str.is_empty(str)
2144 Bar(bool) -> bool
2145 _ -> Bool.false
2146```
2147
2148In contrast, a _closed tag union_ (or _closed union_) like `[Foo Str, Bar Bool]` (without the `*`) represents the set of all possible tags. If I use a `when` on one of these, I can match on `Foo` only and then on `Bar` only, with no need for a catch-all branch. For example:
2149
2150```roc
2151example : [Foo Str, Bar Bool] -> Bool
2152example = |tag|
2153 when tag is
2154 Foo(str) -> Str.is_empty(str)
2155 Bar(bool) -> bool
2156```
2157
2158If we were to remove the type annotations from the previous two code examples, Roc would infer the same types for them anyway.
2159
2160It would infer `tag : [Foo Str, Bar Bool]` for the latter example because the `when tag is` expression only includes a `Foo Str` branch and a `Bar Bool` branch, and nothing else. Since the `when` doesn't handle any other possibilities, these two tags must be the only possible ones.
2161
2162It would infer `tag : [Foo Str, Bar Bool]*` for the former example because the `when tag is` expression includes a `Foo Str` branch and a `Bar Bool` branch but also a `_ ->` branch, indicating that there may be other tags we don't know about. Since the `when` is flexible enough to handle all possible tags, `tag` gets inferred as an open union.
2163
2164Putting these together, whether a tag union is inferred to be open or closed depends on which possibilities the implementation actually handles.
2165
2166> **Aside:** As with open and closed records, we can use type annotations to make tag union types less flexible than what would be inferred. If we added a `_ ->` branch to the second example above, the compiler would still accept `example : [Foo Str, Bar Bool] -> Bool` as the type annotation, even though the catch-all branch would permit the more flexible `example : [Foo Str, Bar Bool]* -> Bool` annotation instead.
2167
2168### [Combining Open Unions](#combining-open-unions) {#combining-open-unions}
2169
2170When we make a new record, it's inferred to be a closed record. For example, in `foo { a: "hi" }`, the type of `{ a: "hi" }` is inferred to be `{ a : Str }`. In contrast, when we make a new tag, it's inferred to be an open union. So in `foo (Bar "hi")`, the type of `Bar "hi"` is inferred to be `[Bar Str]*`.
2171
2172This is because open unions can accumulate additional tags based on how they're used in the program, whereas closed unions cannot. For example, let's look at this conditional:
2173
2174```roc
2175if x > 5 then
2176 "foo"
2177else
2178 7
2179```
2180
2181This will be a type mismatch because the two branches have incompatible types. Strings and numbers are not type-compatible! Now let's look at another example:
2182
2183```roc
2184if x > 5 then
2185 Ok("foo")
2186else
2187 Err("bar")
2188```
2189
2190This shouldn't be a type mismatch, because we can see that the two branches are compatible; they are both tags that could easily coexist in the same tag union. But if the compiler inferred the type of `Ok("foo")` to be the closed union `[Ok Str]`, and likewise for `Err("bar")` and `[Err Str]`, then this would have to be a type mismatch - because those two closed unions are incompatible.
2191
2192Instead, the compiler infers `Ok("foo")` to be the open union `[Ok Str]*`, and `Err("bar")` to be the open union `[Err Str]*`. Then, when using them together in this conditional, the inferred type of the conditional becomes `[Ok Str, Err Str]*` - that is, the combination of the unions in each of its branches. (Branches in a `when` work the same way with open unions.)
2193
2194Earlier we saw how a function which accepts an open union must account for more possibilities, by including catch-all `_ ->` patterns in its `when` expressions. So _accepting_ an open union means you have more requirements. In contrast, when you already _have_ a value which is an open union, you have fewer requirements. A value which is an open union (like `Ok "foo"`, which has the type `[Ok Str]*`) can be provided to anything that's expecting a tag union (no matter whether it's open or closed), as long as the expected tag union includes at least the tags in the open union you're providing.
2195
2196So if I have an `[Ok Str]*` value, I can pass it to functions with any of these types (among others):
2197
2198| Function Type | Can it receive `[Ok Str]*`? |
2199| --------------------------------------- | --------------------------- |
2200| `[Ok Str]* -> Bool` | Yes |
2201| `[Ok Str] -> Bool` | Yes |
2202| `[Ok Str, Err Bool]* -> Bool` | Yes |
2203| `[Ok Str, Err Bool] -> Bool` | Yes |
2204| `[Ok Str, Err Bool, Whatever]* -> Bool` | Yes |
2205| `[Ok Str, Err Bool, Whatever] -> Bool` | Yes |
2206| `Result Str Bool -> Bool` | Yes |
2207| `[Err Bool, Whatever]* -> Bool` | Yes |
2208
2209That 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.
2210
2211However, 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.
2212
2213> **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!
2214>
2215> 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`."
2216
2217In 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:
2218
2219- If you _have_ a closed union, that means it has all the tags it ever will, and can't accumulate more.
2220- If you _have_ an open union, that means it can accumulate more tags through conditional branches.
2221- If you _accept_ a closed union, that means you only have to handle the possibilities listed in the union.
2222- If you _accept_ an open union, that means you have to handle the possibility that it has a tag you can't know about.
2223
2224### [Type Variables in Tag Unions](#type-variables-in-tag-unions) {#type-variables-in-tag-unions}
2225
2226Earlier we saw these two examples, one with an open tag union and the other with a closed one:
2227
2228```roc
2229example : [Foo Str, Bar Bool]* -> Bool
2230example = |tag|
2231 when tag is
2232 Foo(str) -> Str.is_empty(str)
2233 Bar(bool) -> bool
2234 _ -> Bool.false
2235```
2236
2237```roc
2238example : [Foo Str, Bar Bool] -> Bool
2239example = |tag|
2240 when tag is
2241 Foo(str) -> Str.is_empty(str)
2242 Bar(bool) -> bool
2243```
2244
2245Similarly 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:
2246
2247```roc
2248example : [Foo Str, Bar Bool]a -> [Foo Str, Bar Bool]a
2249example = |tag|
2250 when tag is
2251 Foo(str) -> Bar(Str.is_empty(str))
2252 Bar(bool) -> Bar(Bool.false)
2253 other -> other
2254```
2255
2256This 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.
2257
2258So 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).
2259
2260If 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:
2261
2262- The return type includes `Foo Str`, but no branch explicitly returns `Foo`. Couldn't the return type be `[Bar Bool]a` instead?
2263- 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?
2264
2265The 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.
2266
2267For this reason, any time you see a function that only runs a `when` on its only argument, and that `when` includes a branch like `x -> x` or `other -> other`, the function's argument type and return type must necessarily be equivalent.
2268
2269> **Note:** Just like with records, you can also replace the type variable in tag union types with a concrete type. For example, `[Foo Str][Bar Bool][Baz (List Str)]` is equivalent to `[Foo Str, Bar Bool, Baz (List Str)]`.
2270>
2271> Also just like with records, you can use this to compose tag union type aliases. For example, you can write `NetworkError : [Timeout, Disconnected]` and then `Problem : [InvalidInput, UnknownFormat]NetworkError`
2272
2273### [Record Builder](#record-builder) {#record-builder}
2274
2275Record builders are a syntax sugar for sequencing actions and collecting the intermediate results as fields in a record. All you need to build a record is a `map2`-style function that takes two values of the same type and combines them using a provided combiner function. There are many convenient APIs we can build with this simple syntax.
2276
2277For example, let's say we want a record builder to match URLs as follows:
2278
2279```roc
2280combine_matchers : UrlMatcher a, UrlMatcher b, (a, b -> c) -> UrlMatcher c
2281combine_matchers = |matcher_a, matcher_b, combiner| ...
2282
2283user_tab_matcher : UrlMatcher { users: {}, user_id: U64, tab: Str }
2284user_tab_matcher =
2285 { combine_matchers <-
2286 users: exact_segment("users"),
2287 user_id: u64_segment,
2288 tab: any_segment,
2289 }
2290
2291expect
2292 user_tab_matcher
2293 |> match_on_url("/users/123/account")
2294 == Ok({ users: {}, user_id: 123, tab: "account" })
2295```
2296
2297The `user_tab_matcher` record builder desugars to the following:
2298
2299```roc
2300user_tab_matcher =
2301 combine_matchers(
2302 exact_segment("users"),
2303 combine_matchers(
2304 u64_segment,
2305 any_segment,
2306 |user_id, tab| (user_id, tab),
2307 ),
2308 |users, (user_id, tab)| { users, user_id, tab },
2309 )
2310```
2311
2312You can see that the `combine_matchers` builder function is simply applied in sequence, pairing up all fields until a record is created.
2313
2314You'll notice that the `users` field above holds an empty record, and isn't a useful part of the result. If you want to ignore such a field in the record builder, prefix its name with an underscore as you would do to ignore a variable:
2315
2316```roc
2317user_tab_matcher : UrlMatcher { user_id: U64 }
2318user_tab_matcher =
2319 { combine_matchers <-
2320 _: exact_segment("users"),
2321 user_id: u64_segment,
2322 _tab: any_segment,
2323 }
2324
2325expect
2326 user_tab_matcher
2327 |> match_on_url("/users/123/account")
2328 == Ok({ user_id: 123 })
2329```
2330
2331If you want to see other examples of using record builders, look at the [Record Builder Example](https://www.roc-lang.org/examples/RecordBuilder/README.html).
2332
2333### [Reserved Keywords](#reserved-keywords) {#reserved-keywords}
2334
2335These are reserved keywords in Roc. You can't choose any of them as names, except as record field names.
2336
2337`as`, `crash`, `dbg`, `else`, `expect`, `expect-fx`, `if`, `import`, `is`, `return`, `then`, `try`, `when`
2338
2339Other keywords are used only in specific places, so they are not reserved. This includes:
2340
2341`app`, `exposes`, `exposing`, `generates`, `implements`, `module`, `package`, `packages`, `platform`, `requires`, `where`, `with`
2342
2343## [Operator Desugaring Table](#operator-desugaring-table) {#operator-desugaring-table}
2344
2345Here are various Roc expressions involving operators, and what they desugar to.
2346
2347| Expression | Desugars To |
2348| ---------------------------- | ---------------------------------------------------------------------------- |
2349| `a + b` | `Num.add(a, b)` |
2350| `a - b` | `Num.sub(a, b)` |
2351| `a * b` | `Num.mul(a, b)` |
2352| `a / b` | `Num.div(a, b)` |
2353| `a // b` | `Num.div_trunc(a, b)` |
2354| `a ^ b` | `Num.pow(a, b)` |
2355| `a % b` | `Num.rem(a, b)` |
2356| `-a` | `Num.neg(a)` |
2357| `a == b` | `Bool.is_eq(a, b)` |
2358| `a != b` | `Bool.is_not_eq(a, b)` |
2359| `a && b` | `Bool.and(a, b)` |
2360| <code>a \|\| b</code> | `Bool.or(a, b)` |
2361| `!a` | `Bool.not(a)` |
2362| <code>a \|> f</code> | `f(a)` |
2363| <code>f a b \|> g x y</code> | `g(f(a, b), x y)` |
2364| `f?` | [see example](https://www.roc-lang.org/examples/TryOperatorDesugaring/README) |
2365
2366### [Additional Resources](#additional-resources) {#additional-resources}
2367
2368You've completed the tutorial, well done!
2369
2370If you are looking for more resources to learn Roc, check out [these links](/install#additional-learning-resources).
2371
2372</section>
2373<script type="text/javascript" src="/builtins/main/search.js" defer></script>