create-mendix-widget-gleam 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1884 @@
1
+
2
+ ---
3
+
4
+ ## Hello world
5
+
6
+ Here is a tiny program that prints out the text "Hello, Joe!". We'll explain how it works shortly.
7
+
8
+ In a normal Gleam project this program would be run using the command `gleam run` on the command line, but here in this tour the program is compiled and run inside your web browser, allowing you to try Gleam without installing anything on your computer.
9
+
10
+ Try changing the text being printed to `Hello, Mike!` and see what happens.
11
+
12
+ ```gleam
13
+ import gleam/io
14
+
15
+ pub fn main() {
16
+ io.println("Hello, Joe!")
17
+ }
18
+ ```
19
+
20
+ ---
21
+
22
+ ## Modules
23
+
24
+ Gleam code is organized into units called _modules_. A module is a bunch of definitions (of types, functions, etc.) that seem to belong together. For example, the [`gleam/io`](https://hexdocs.pm/gleam_stdlib/gleam/io.html) module contains a variety of functions for printing, like [`println`](https://hexdocs.pm/gleam_stdlib/gleam/io.html#println) .
25
+
26
+ All gleam code is in _some_ module or other, whose name comes from the name of the file it's in. For example, [`gleam/io`](https://hexdocs.pm/gleam_stdlib/gleam/io.html) is in a file called `io.gleam` in a directory called `gleam`.
27
+
28
+ For code in one module to access code in another module, we import it using the `import` keyword, and the name used to refer to it is the last part of the module name. For example, the [`gleam/io`](https://hexdocs.pm/gleam_stdlib/gleam/io.html) module is referred to as `io` once imported.
29
+
30
+ The `as` keyword can be used to refer to a module by a different name. See how the [`gleam/string`](https://hexdocs.pm/gleam_stdlib/gleam/string.html) module is referred to as `text` here.
31
+
32
+ Comments in Gleam start with `//` and continue to the end of the line. Comments go on the line before the item they are about, not after.
33
+
34
+ ```gleam
35
+ import gleam/io
36
+ import gleam/string as text
37
+
38
+ pub fn main() {
39
+ // Use a function from the `gleam/io` module
40
+ io.println("Hello, Mike!")
41
+
42
+ // Use a function from the `gleam/string` module
43
+ io.println(text.reverse("Hello, Joe!"))
44
+ }
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Unqualified imports
50
+
51
+ Normally functions from other modules are used in a _qualified_ fashion, meaning the name used to refer the module goes before function name with a dot between them. For example, [`io.println("Hello!")`](https://hexdocs.pm/gleam_stdlib/gleam/io.html#println) .
52
+
53
+ It is also possible to specify a list of functions to import from a module in an _unqualified_ fashion, meaning the function name can be used without the module _qualifier_ (the name and the dot) before it.
54
+
55
+ Generally it is best to use qualified imports, as this makes it clear where the function is defined, making the code easier to read.
56
+
57
+ ```gleam
58
+ // Import the module and one of its functions
59
+ import gleam/io.{println}
60
+
61
+ pub fn main() {
62
+ // Use the function in a qualified fashion
63
+ io.println("This is qualified")
64
+
65
+ // Or an unqualified fashion
66
+ println("This is unqualified")
67
+ }
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Type checking
73
+
74
+ Gleam has a robust static type system that helps you as you write and edit code, catching mistakes and showing you where to make changes.
75
+
76
+ Uncomment the line [`io.println(4)`](https://hexdocs.pm/gleam_stdlib/gleam/io.html#println) and see how a compile time error is reported as the [`io.println`](https://hexdocs.pm/gleam_stdlib/gleam/io.html#println) function only works with strings, not ints.
77
+
78
+ If you need to debug print something you can use the `echo` keyword instead, as it will print a value of any type.
79
+
80
+ Gleam has no `null`, no implicit conversions, no exceptions, and always performs full type checking. If the code compiles you can be reasonably confident it does not have any inconsistencies that may cause bugs or crashes.
81
+
82
+ ```gleam
83
+ import gleam/io
84
+
85
+ pub fn main() {
86
+ io.println("My lucky number is:")
87
+ // io.println(4)
88
+ // 👆️ Uncomment this line to see the error
89
+
90
+ // echo 4
91
+ // 👆️ You can use `echo` to debug print a value of any type!
92
+ }
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Ints
98
+
99
+ Gleam's `Int` type represents whole numbers.
100
+
101
+ There are arithmetic and comparison operators for ints, as well as the equality operator which works on all types.
102
+
103
+ When running on the Erlang virtual machine ints have no maximum and minimum size. When running on JavaScript runtimes ints are represented using JavaScript's 64 bit floating point numbers.
104
+
105
+ The [`gleam/int`](https://hexdocs.pm/gleam_stdlib/gleam/int.html) standard library module contains functions for working with ints.
106
+
107
+ ```gleam
108
+ import gleam/int
109
+
110
+ pub fn main() {
111
+ // Int arithmetic
112
+ echo 1 + 1
113
+ echo 5 - 1
114
+ echo 5 / 2
115
+ echo 3 * 3
116
+ echo 5 % 2
117
+
118
+ // Int comparisons
119
+ echo 3 > 1 + 1
120
+ echo 2 < 1 - 1
121
+ echo 8 >= 1 + 3
122
+ echo 8 <= 5 - 3
123
+
124
+ // Equality works for any type
125
+ echo 2 == 1 + 1
126
+ echo 2 == 1 - 1
127
+
128
+ // Standard library int functions
129
+ echo int.max(42, 77)
130
+ echo int.clamp(5, 10, 20)
131
+ }
132
+ ```
133
+
134
+ ---
135
+
136
+ ## Floats
137
+
138
+ Gleam's `Float` type represents numbers that are not integers.
139
+
140
+ Gleam's numerical operators are not overloaded, so there are dedicated operators for working with floats.
141
+
142
+ Floats are represented as 64 bit floating point numbers on both the Erlang and JavaScript runtimes. The floating point behaviour is native to their respective runtimes, so their exact behaviour will be slightly different on the two runtimes.
143
+
144
+ Under the JavaScript runtime, exceeding the maximum (or minimum) representable value for a floating point value will result in `Infinity` (or `-Infinity`). Should you try to divide two infinities you will get `NaN` as a result.
145
+
146
+ When running on the BEAM any overflow will raise an error. So there is no `NaN` or `Infinity` float value in the Erlang runtime.
147
+
148
+ Division by zero will not overflow, but is instead defined to be zero.
149
+
150
+ The [`gleam/float`](https://hexdocs.pm/gleam_stdlib/gleam/float.html) standard library module contains functions for working with floats.
151
+
152
+ ```gleam
153
+ import gleam/float
154
+
155
+ pub fn main() {
156
+ // Float arithmetic
157
+ echo 1.0 +. 1.5
158
+ echo 5.0 -. 1.5
159
+ echo 5.0 /. 2.5
160
+ echo 3.0 *. 3.5
161
+
162
+ // Float comparisons
163
+ let one = 1.0
164
+ echo 2.2 >. one
165
+ echo 2.2 <. one
166
+ echo 2.2 >=. one
167
+ echo 2.2 <=. one
168
+
169
+ // Equality works for any type
170
+ echo 3.0 == 1.5 *. 2.0
171
+ echo 2.1 == 1.2 +. 1.0
172
+
173
+ // Division by zero is not an error
174
+ echo 3.14 /. 0.0
175
+
176
+ // Standard library float functions
177
+ echo float.max(2.0, 9.5)
178
+ echo float.ceiling(5.4)
179
+ }
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Number formats
185
+
186
+ Underscores can be added to numbers for clarity. For example, `1000000` can be tricky to read quickly, while `1_000_000` can be easier.
187
+
188
+ Ints can be written in binary, octal, or hexadecimal formats using the `0b`, `0o`, and `0x` prefixes respectively.
189
+
190
+ Floats can be written in a scientific notation.
191
+
192
+ ```gleam
193
+ pub fn main() {
194
+ // Underscores
195
+ echo 1_000_000
196
+ echo 10_000.01
197
+
198
+ // Binary, octal, and hex Int literals
199
+ echo 0b00001111
200
+ echo 0o17
201
+ echo 0xF
202
+
203
+ // Scientific notation Float literals
204
+ echo 7.0e7
205
+ echo 3.0e-4
206
+ }
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Equality
212
+
213
+ Gleam has the `==` and `!=` operators for checking equality.
214
+
215
+ The operators can be used with values of any type, but both sides of the operator must be of the same type.
216
+
217
+ Equality is checked _structurally_, meaning that two values are equal if they have the same structure rather than if they are at the same memory location.
218
+
219
+ ```gleam
220
+ pub fn main() {
221
+ echo 100 == 50 + 50
222
+ echo 1.5 != 0.1 *. 10.0
223
+ }
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Strings
229
+
230
+ In Gleam strings are written as text surrounded by double quotes, and can span multiple lines and contain unicode characters.
231
+
232
+ The `<>` operator can be used to concatenate strings.
233
+
234
+ Several escape sequences are supported:
235
+
236
+ * `\"` - double quote
237
+ * `\\` - backslash
238
+ * `\f` - form feed
239
+ * `\n` - newline
240
+ * `\r` - carriage return
241
+ * `\t` - tab
242
+ * `\u{xxxxxx}` - unicode codepoint
243
+
244
+ The [`gleam/string`](https://hexdocs.pm/gleam_stdlib/gleam/string.html) standard library module contains functions for working with strings.
245
+
246
+ ```gleam
247
+ import gleam/io
248
+ import gleam/string
249
+
250
+ pub fn main() {
251
+ // String literals
252
+ io.println("👩‍💻 こんにちは Gleam 🏳️‍🌈")
253
+ io.println(
254
+ "multi
255
+ line
256
+ string",
257
+ )
258
+ io.println("\u{1F600}")
259
+
260
+ // Double quote can be escaped
261
+ io.println("\"X\" marks the spot")
262
+
263
+ // String concatenation
264
+ io.println("One " <> "Two")
265
+
266
+ // String functions
267
+ io.println(string.reverse("1 2 3 4 5"))
268
+ io.println(string.append("abc", "def"))
269
+ }
270
+ ```
271
+
272
+ ---
273
+
274
+ ## Bools
275
+
276
+ A `Bool` is either `True` or `False`.
277
+
278
+ The `||`, `&&`, and `!` operators can be used to manipulate bools.
279
+
280
+ The `||` and `&&` operators are short-circuiting, meaning that if the left hand side of the operator is `True` for `||` or `False` for `&&` then the right hand side of the operator will not be evaluated.
281
+
282
+ The [`gleam/bool`](https://hexdocs.pm/gleam_stdlib/gleam/bool.html) standard library module contains functions for working with bools.
283
+
284
+ ```gleam
285
+ import gleam/bool
286
+
287
+ pub fn main() {
288
+ // Bool operators
289
+ echo True && False
290
+ echo True && True
291
+ echo False || False
292
+ echo False || True
293
+
294
+ // Bool functions
295
+ echo bool.to_string(True)
296
+ }
297
+ ```
298
+
299
+ ---
300
+
301
+ ## Assignments
302
+
303
+ A value can be assigned to a variable using `let`.
304
+
305
+ Variable names can be reused by later let bindings, but the values they reference are immutable, so the values themselves are not changed or mutated in any way.
306
+
307
+ In Gleam variable and function names are written in `snake_case`.
308
+
309
+ ```gleam
310
+ import gleam/io
311
+
312
+ pub fn main() {
313
+ let x = "Original"
314
+ io.println(x)
315
+
316
+ // Assign `y` to the value of `x`
317
+ let y = x
318
+ io.println(y)
319
+
320
+ // Assign `x` to a new value
321
+ let x = "New"
322
+ io.println(x)
323
+
324
+ // The `y` still refers to the original value
325
+ io.println(y)
326
+ }
327
+ ```
328
+
329
+ ---
330
+
331
+ ## Discard patterns
332
+
333
+ If a variable is assigned but not used then Gleam will emit a warning.
334
+
335
+ If a variable is intended to not be used, then the name can be prefixed with an underscore, silencing the warning.
336
+
337
+ Try changing the variable name to `score` to see the warning.
338
+
339
+ ```gleam
340
+ pub fn main() {
341
+ // This variable is never used
342
+ let _score = 1000
343
+ }
344
+ ```
345
+
346
+ ---
347
+
348
+ ## Type annotations
349
+
350
+ Let assignments can be written with a type annotation after the name.
351
+
352
+ Type annotations may be useful for documentation purposes, but they do not change how Gleam type checks the code beyond ensuring that the annotation is correct.
353
+
354
+ Typically Gleam code will not have type annotations for assignments.
355
+
356
+ Try changing a type annotation to something incorrect to see the compile error.
357
+
358
+ ```gleam
359
+ pub fn main() {
360
+ let _name: String = "Gleam"
361
+
362
+ let _is_cool: Bool = True
363
+
364
+ let _version: Int = 1
365
+ }
366
+ ```
367
+
368
+ ---
369
+
370
+ ## Type imports
371
+
372
+ Other modules may also define types that we wish to refer to. In this case we need to import them.
373
+
374
+ Like functions, types can be referred to in a _qualified_ way by putting the imported module name and a dot before the type name. For example, `bytes_tree.BytesTree`
375
+
376
+ Types can also be imported in an _unqualified_ way by listing them in the import statement with the word `type` before the type name.
377
+
378
+ Unlike functions, Gleam types are commonly imported in an unqualified way.
379
+
380
+ ```gleam
381
+ import gleam/bytes_tree
382
+ import gleam/string_tree.{type StringTree}
383
+
384
+ pub fn main() {
385
+ // Referring to a type in a qualified way
386
+ let _bytes: bytes_tree.BytesTree = bytes_tree.new()
387
+
388
+ // Refering to a type in an unqualified way
389
+ let _text: StringTree = string_tree.new()
390
+ }
391
+ ```
392
+
393
+ ---
394
+
395
+ ## Type aliases
396
+
397
+ A type alias can be used to refer to a type by a different name. Giving a type an alias doesn't make a new type, it is still the same type.
398
+
399
+ A type's name always starts with a capital letter, contrasting to variables and functions, which start with a lowercase letter.
400
+
401
+ When the `pub` keyword is used the type alias is public and can be referred to by other modules.
402
+
403
+ Type aliases should be used **rarely**. Aliasing a type makes it less clear what it is, and does not provide the type safety benefits that using a custom type would give.
404
+
405
+ ```gleam
406
+ pub type Number =
407
+ Int
408
+
409
+ pub fn main() {
410
+ let one: Number = 1
411
+ let two: Int = 2
412
+
413
+ // Number and Int are two names for the same type
414
+ echo one == two
415
+ }
416
+ ```
417
+
418
+ ---
419
+
420
+ ## Blocks
421
+
422
+ Blocks are one or more expressions grouped together with curly braces. Each expression is evaluated in order and the value of the last expression is returned.
423
+
424
+ Any variables assigned within the block can only be used within the block.
425
+
426
+ Try uncommenting `echo degrees` to see the compile error from trying to use a variable that is not in scope.
427
+
428
+ Blocks can also be used to change the order of evaluation of binary operators expressions.
429
+
430
+ `*` binds more tightly than `+` so the expression `1 + 2 * 3` evaluates to 7. If the `1 + 2` should be evaluated first to make the expression evaluate to 9 then the expression can be wrapped in a block: `{ 1 + 2 } * 3`. This is similar to grouping with parentheses in some other languages.
431
+
432
+ ```gleam
433
+ pub fn main() {
434
+ let fahrenheit = {
435
+ let degrees = 64
436
+ degrees
437
+ }
438
+ // echo degrees
439
+ // ^^^^^^^ This will not compile
440
+
441
+ // Changing order of evaluation
442
+ let celsius = { fahrenheit - 32 } * 5 / 9
443
+ echo celsius
444
+ }
445
+ ```
446
+
447
+ ---
448
+
449
+ ## Lists
450
+
451
+ Lists are ordered collections of values.
452
+
453
+ [`List`](https://hexdocs.pm/gleam_stdlib/gleam/list.html) is a generic type, having a type parameter for the type of values it contains. A list of ints has the type `List(Int)`, and a list of strings has the type `List(String)`.
454
+
455
+ Lists are immutable single-linked lists, meaning they are very efficient to add and remove elements from the front of the list.
456
+
457
+ Counting the length of a list or getting elements from other positions in the list is expensive and rarely done. It is rare to write algorithms that index into sequences in Gleam, but when they are written a list is not the right choice of data structure.
458
+
459
+ ```gleam
460
+ pub fn main() {
461
+ let ints = [1, 2, 3]
462
+
463
+ echo ints
464
+
465
+ // Immutably prepend
466
+ echo [-1, 0, ..ints]
467
+
468
+ // Uncomment this to see the error
469
+ // echo ["zero", ..ints]
470
+
471
+ // The original lists are unchanged
472
+ echo ints
473
+ }
474
+ ```
475
+
476
+ ---
477
+
478
+ ## Constants
479
+
480
+ As well as let assignments Gleam also has constants, which are defined at the top level of a module.
481
+
482
+ Constants must be literal values, functions cannot be used in their definitions.
483
+
484
+ Constants may be useful for values that are used throughout your program, permitting them to be named and to ensure there are no differences in the definition between each use.
485
+
486
+ Using a constant may be more efficient than creating the same value in multiple functions, though the exact performance characteristics will depend on the runtime and whether compiling to Erlang or JavaScript.
487
+
488
+ ```gleam
489
+ const ints: List(Int) = [1, 2, 3]
490
+
491
+ const floats = [1.1, 2.2, 3.3]
492
+
493
+ pub fn main() {
494
+ echo ints
495
+ echo ints == [1, 2, 3]
496
+
497
+ echo floats
498
+ echo floats == [1.1, 2.2, 3.3]
499
+ }
500
+ ```
501
+
502
+ ---
503
+
504
+ ## Functions
505
+
506
+ The `fn` keyword is used to define new functions.
507
+
508
+ Similar to blocks, each expression in the function body is evaluated in order and the value of the last expression is returned from the function. Gleam is an expression based language so there is no `return` operator, but there are ways to conditionally return early from a function, which will be introduced in a later section.
509
+
510
+ The `double` and `multiply` functions are defined without the `pub` keyword. This makes them _private_ functions, they can only be used within this module. If another module attempted to use them it would result in a compiler error.
511
+
512
+ Like with assignments, type annotations are optional for function arguments and return values. It is considered good practice to use type annotations for functions, for clarity and to encourage intentional and thoughtful design.
513
+
514
+ ```gleam
515
+ pub fn main() {
516
+ echo double(10)
517
+ }
518
+
519
+ fn double(a: Int) -> Int {
520
+ multiply(a, 2)
521
+ }
522
+
523
+ fn multiply(a: Int, b: Int) -> Int {
524
+ a * b
525
+ }
526
+ ```
527
+
528
+ ---
529
+
530
+ ## Higher order functions
531
+
532
+ In Gleam functions are values. They can be assigned to variables, passed to other functions, and anything else you can do with values.
533
+
534
+ Here the function `add_one` is being passed as an argument to the `twice` function.
535
+
536
+ Notice the `fn` keyword is also used to describe the type of the function that `twice` takes as its second argument.
537
+
538
+ ```gleam
539
+ pub fn main() {
540
+ // Call a function with another function
541
+ echo twice(1, add_one)
542
+
543
+ // Functions can be assigned to variables
544
+ let my_function = add_one
545
+ echo my_function(100)
546
+ }
547
+
548
+ fn twice(argument: Int, passed_function: fn(Int) -> Int) -> Int {
549
+ passed_function(passed_function(argument))
550
+ }
551
+
552
+ fn add_one(argument: Int) -> Int {
553
+ argument + 1
554
+ }
555
+ ```
556
+
557
+ ---
558
+
559
+ ## Anonymous functions
560
+
561
+ As well as module-level named functions, Gleam has anonymous function literals, written with the `fn() { ... }` syntax.
562
+
563
+ Anonymous functions can be used interchangeably with named functions.
564
+
565
+ Anonymous functions can reference variables that were in scope when they were defined, making them _closures_.
566
+
567
+ ```gleam
568
+ pub fn main() {
569
+ // Assign an anonymous function to a variable
570
+ let add_one = fn(a) { a + 1 }
571
+ echo twice(1, add_one)
572
+
573
+ // Pass an anonymous function as an argument
574
+ echo twice(1, fn(a) { a * 2 })
575
+
576
+ let secret_number = 42
577
+ // This anonymous function always returns 42
578
+ let secret = fn() { secret_number }
579
+ echo secret()
580
+ }
581
+
582
+ fn twice(argument: Int, my_function: fn(Int) -> Int) -> Int {
583
+ my_function(my_function(argument))
584
+ }
585
+ ```
586
+
587
+ ---
588
+
589
+ ## Function captures
590
+
591
+ Gleam has a shorthand syntax for creating anonymous functions that take one argument and immediately call another function with that argument: the function capture syntax.
592
+
593
+ The anonymous function `fn(a) { some_function(..., a, ...) }` can be written as `some_function(..., _, ...)`, with any number of other arguments passed directly to the inner function. The underscore `_` is a placeholder for the argument, equivalent to `a` in `fn(a) { some_function(..., a, ...) }`.
594
+
595
+ ```gleam
596
+ pub fn main() {
597
+ // These two statements are equivalent
598
+ let add_one_v1 = fn(x) { add(1, x) }
599
+ let add_one_v2 = add(1, _)
600
+
601
+ echo add_one_v1(10)
602
+ echo add_one_v2(10)
603
+ }
604
+
605
+ fn add(a: Int, b: Int) -> Int {
606
+ a + b
607
+ }
608
+ ```
609
+
610
+ ---
611
+
612
+ ## Generic functions
613
+
614
+ Up until now each function has accepted precisely one type for each of its arguments.
615
+
616
+ The `twice` function in the previous lesson on _higher order functions_ only worked with functions that would take and return ints. This is overly restrictive, it should be possible to use this function with any type, so long as the function and the initial value are compatible.
617
+
618
+ To enable this, Gleam supports _generics_, also known as _parametric polymorphism_.
619
+
620
+ This works by using a type variable instead of specifying a concrete type. It stands in for whatever specific type is being used when the function is called. These type variables are written with a lowercase name.
621
+
622
+ Type variables are not like an `any` type, they get replaced with a specific type each time the function is called. Try uncommenting `twice(10, exclaim)` to see the compiler error from trying to use a type variable as an int and a string at the same time.
623
+
624
+ ```gleam
625
+ pub fn main() {
626
+ let add_one = fn(x) { x + 1 }
627
+ let exclaim = fn(x) { x <> "!" }
628
+
629
+ // Invalid, Int and String are not the same type
630
+ // twice(10, exclaim)
631
+
632
+ // Here the type variable is replaced by the type Int
633
+ echo twice(10, add_one)
634
+
635
+ // Here the type variable is replaced by the type String
636
+ echo twice("Hello", exclaim)
637
+ }
638
+
639
+ // The name `value` refers to the same type multiple times
640
+ fn twice(argument: value, my_function: fn(value) -> value) -> value {
641
+ my_function(my_function(argument))
642
+ }
643
+ ```
644
+
645
+ ---
646
+
647
+ ## Pipelines
648
+
649
+ It's common to want to call a series of functions, passing the result of one to the next. With the regular function call syntax this can be a little difficult to read as you have to read the code from the inside out.
650
+
651
+ Gleam's pipe operator `|>` helps with this problem by allowing you to write code top-to-bottom.
652
+
653
+ The pipe operator takes the result of the expression on its left and passes it as an argument to the function on its right.
654
+
655
+ It will first check to see if the left-hand value could be used as the first argument to the call. For example, `a |> b(1, 2)` would become `b(a, 1, 2)`. If not, it falls back to calling the result of the right-hand side as a function, e.g., `b(1, 2)(a)`
656
+
657
+ Gleam code is typically written with the "subject" of the function as the first argument, to make it easier to pipe. If you wish to pipe to a different position then a function capture can be used to insert the argument to the desired position.
658
+
659
+ If you need to debug print a value in the middle of a pipeline you can use `|> echo` to do it.
660
+
661
+ ```gleam
662
+ import gleam/io
663
+ import gleam/string
664
+
665
+ pub fn main() {
666
+ // Without the pipe operator
667
+ io.println(string.drop_start(string.drop_end("Hello, Joe!", 1), 7))
668
+
669
+ // With the pipe operator
670
+ "Hello, Mike!"
671
+ |> string.drop_end(1)
672
+ |> string.drop_start(7)
673
+ |> io.println
674
+
675
+ // Changing order with function capturing
676
+ "1"
677
+ |> string.append("2")
678
+ |> string.append("3", _)
679
+ |> io.println
680
+ }
681
+ ```
682
+
683
+ ---
684
+
685
+ ## Labelled arguments
686
+
687
+ When functions take several arguments it can be difficult to remember what the arguments are, and what order they are expected in.
688
+
689
+ To help with this Gleam supports labelled arguments, where function arguments are given an external label in addition to their internal name. These labels are written before the argument name in the function definition.
690
+
691
+ When labelled arguments are used the order of the arguments does not matter, but all unlabelled arguments must come before labelled arguments.
692
+
693
+ There is no performance cost to using labelled arguments, it does not allocate a dictionary or perform any other runtime work.
694
+
695
+ Labels are optional when calling a function, it is up to the programmer to decide what is clearest in their code.
696
+
697
+ ```gleam
698
+ pub fn main() {
699
+ // Without using labels
700
+ echo calculate(1, 2, 3)
701
+
702
+ // Using the labels
703
+ echo calculate(1, add: 2, multiply: 3)
704
+
705
+ // Using the labels in a different order
706
+ echo calculate(1, multiply: 3, add: 2)
707
+ }
708
+
709
+ fn calculate(value: Int, add addend: Int, multiply multiplier: Int) {
710
+ value * multiplier + addend
711
+ }
712
+ ```
713
+
714
+ ---
715
+
716
+ ## Label shorthand syntax
717
+
718
+ When local variables have the same names as a function's labelled arguments, the variable names can be omitted when calling the function. This is known as shorthand syntax for labels.
719
+
720
+ The shorthand syntax can also be used for record constructor arguments.
721
+
722
+ ```gleam
723
+ pub fn main() {
724
+ let quantity = 5.0
725
+ let unit_price = 10.0
726
+ let discount = 0.2
727
+
728
+ // Using the regular label syntax
729
+ echo calculate_total_cost(
730
+ quantity: quantity,
731
+ unit_price: unit_price,
732
+ discount: discount,
733
+ )
734
+
735
+ // Using the shorthand syntax
736
+ echo calculate_total_cost(quantity:, unit_price:, discount:)
737
+ }
738
+
739
+ fn calculate_total_cost(
740
+ quantity quantity: Float,
741
+ unit_price price: Float,
742
+ discount discount: Float,
743
+ ) -> Float {
744
+ let subtotal = quantity *. price
745
+ let discount = subtotal *. discount
746
+ subtotal -. discount
747
+ }
748
+ ```
749
+
750
+ ---
751
+
752
+ ## Documentation comments
753
+
754
+ Documentation and comments are important tools for making your code easier to work with and understand.
755
+
756
+ As well as regular `//` comments Gleam has `///` and `////` comments which are used for attaching documentation to code.
757
+
758
+ `///` is used for documenting types and functions, and should be placed immediately before the type or function it is documenting.
759
+
760
+ `////` is used for documenting modules, and should be placed at the top of the module.
761
+
762
+ ```gleam
763
+ //// A module containing some unusual functions and types.
764
+
765
+ /// A type where the value can never be constructed.
766
+ /// Can you work out why?
767
+ pub type Never {
768
+ Never(Never)
769
+ }
770
+
771
+ /// Call a function twice with an initial value.
772
+ ///
773
+ pub fn twice(argument: value, my_function: fn(value) -> value) -> value {
774
+ my_function(my_function(argument))
775
+ }
776
+
777
+ /// Call a function three times with an initial value.
778
+ ///
779
+ pub fn thrice(argument: value, my_function: fn(value) -> value) -> value {
780
+ my_function(my_function(my_function(argument)))
781
+ }
782
+ ```
783
+
784
+ ---
785
+
786
+ ## Deprecations
787
+
788
+ Functions and other definitions can be marked as deprecated using the `@deprecated` attribute.
789
+
790
+ If a deprecated function is referenced the compiler will emit a warning, letting the programmer know they ought to update their code.
791
+
792
+ The deprecation attribute takes a message and this will be displayed to the user in the warning. In the message explain to the user the new approach or replacement function, or direct them to documentation on how to upgrade.
793
+
794
+ ```gleam
795
+ pub fn main() {
796
+ echo old_function()
797
+ echo new_function()
798
+ }
799
+
800
+ @deprecated("Use new_function instead")
801
+ fn old_function() {
802
+ Nil
803
+ }
804
+
805
+ fn new_function() {
806
+ Nil
807
+ }
808
+ ```
809
+
810
+ ---
811
+
812
+ ## Case expressions
813
+
814
+ The case expression is the most common kind of flow control in Gleam code. It is similar to `switch` in some other languages, but more powerful than most.
815
+
816
+ It allows the programmer to say "if the data has this shape then run this code", a process called _pattern matching_.
817
+
818
+ Gleam performs _exhaustiveness checking_ to ensure that the patterns in a case expression cover all possible values. With this you can have confidence that your logic is up-to-date for the design of the data you are working with.
819
+
820
+ Try commenting out patterns or adding new redundant ones, and see what problems the compiler reports.
821
+
822
+ ```gleam
823
+ import gleam/int
824
+
825
+ pub fn main() {
826
+ let x = int.random(5)
827
+ echo x
828
+
829
+ let result = case x {
830
+ // Match specific values
831
+ 0 -> "Zero"
832
+ 1 -> "One"
833
+
834
+ // Match any other value
835
+ _ -> "Other"
836
+ }
837
+ echo result
838
+ }
839
+ ```
840
+
841
+ ---
842
+
843
+ ## Variable patterns
844
+
845
+ Patterns in case expressions can also assign variables.
846
+
847
+ When a variable name is used in a pattern the value that is matched against is assigned to that name, and can be used in the body of that clause.
848
+
849
+ ```gleam
850
+ import gleam/int
851
+
852
+ pub fn main() {
853
+ let result = case int.random(5) {
854
+ // Match specific values
855
+ 0 -> "Zero"
856
+ 1 -> "One"
857
+
858
+ // Match any other value and assign it to a variable
859
+ other -> "It is " <> int.to_string(other)
860
+ }
861
+ echo result
862
+ }
863
+ ```
864
+
865
+ ---
866
+
867
+ ## String patterns
868
+
869
+ When pattern matching on strings the `<>` operator can be used to match on strings with a specific prefix.
870
+
871
+ The pattern `"Hello, " <> name` matches any string that starts with `"Hello, "` and assigns the rest of the string to the variable `name`.
872
+
873
+ ```gleam
874
+ pub fn main() {
875
+ echo get_name("Hello, Joe")
876
+ echo get_name("Hello, Mike")
877
+ echo get_name("System still working?")
878
+ }
879
+
880
+ fn get_name(x: String) -> String {
881
+ case x {
882
+ "Hello, " <> name -> name
883
+ _ -> "Unknown"
884
+ }
885
+ }
886
+ ```
887
+
888
+ ---
889
+
890
+ ## List patterns
891
+
892
+ Lists and the values they contain can be pattern matched on in case expressions.
893
+
894
+ List patterns match on specific lengths of lists. The pattern `[]` matches an empty list, and the pattern `[_]` matches a list with one element. They will not match on lists with other lengths.
895
+
896
+ The list append pattern `..` can be used to match the rest of the list. The pattern `[1, ..]` matches any list that starts with `1`. The pattern `[_, _, ..]` matches any list that has at least two elements.
897
+
898
+ ```gleam
899
+ import gleam/int
900
+ import gleam/list
901
+
902
+ pub fn main() {
903
+ let x = list.repeat(int.random(5), times: int.random(3))
904
+ echo x
905
+
906
+ let result = case x {
907
+ [] -> "Empty list"
908
+ [1] -> "List of just 1"
909
+ [4, ..] -> "List starting with 4"
910
+ [_, _] -> "List of 2 elements"
911
+ _ -> "Some other list"
912
+ }
913
+ echo result
914
+ }
915
+ ```
916
+
917
+ ---
918
+
919
+ ## Recursion
920
+
921
+ Gleam doesn't have loops, instead iteration is done through recursion, that is through top-level functions calling themselves with different arguments.
922
+
923
+ A recursive function needs to have at least one _base case_ and at least one _recursive case_. A base case returns a value without calling the function again. A recursive case calls the function again with different inputs, looping again.
924
+
925
+ The Gleam standard library has functions for various common looping patterns, some of which will be introduced in later lessons, however for more complex loops manual recursion is often the clearest way to write it.
926
+
927
+ Recursion can seem daunting or unclear at first if you are more familiar with languages that have special looping features, but stick with it! With time it'll become just as familiar and comfortable as any other way of iterating.
928
+
929
+ ```gleam
930
+ pub fn main() {
931
+ echo factorial(5)
932
+ echo factorial(7)
933
+ }
934
+
935
+ // A recursive functions that calculates factorial
936
+ pub fn factorial(x: Int) -> Int {
937
+ case x {
938
+ // Base case
939
+ 0 -> 1
940
+ 1 -> 1
941
+
942
+ // Recursive case
943
+ _ -> x * factorial(step_towards_zero(x))
944
+ }
945
+ }
946
+
947
+ fn step_towards_zero(x: Int) -> Int {
948
+ case x >= 0 {
949
+ True -> x - 1
950
+ False -> x + 1
951
+ }
952
+ }
953
+ ```
954
+
955
+ ---
956
+
957
+ ## Tail calls
958
+
959
+ When a function is called a new stack frame is created in memory to store the arguments and local variables of the function. If lots of these frames are created during recursion then the program would use a large amount of memory, or even crash the program if some limit is hit.
960
+
961
+ To avoid this problem Gleam supports _tail call optimisation_, which allows the stack frame for the current function to be reused if a function call is the last thing the function does, removing the memory cost.
962
+
963
+ Unoptimised recursive functions can often be rewritten into tail call optimised functions by using an accumulator. An accumulator is a variable that is passed along in addition to the data, similar to a mutable variable in a language with `while` loops.
964
+
965
+ Accumulators should be hidden away from the users of your code; they are internal implementation details. To do this write a public function that calls a recursive private function with the initial accumulator value.
966
+
967
+ ```gleam
968
+ pub fn main() {
969
+ echo factorial(5)
970
+ echo factorial(7)
971
+ }
972
+
973
+ pub fn factorial(x: Int) -> Int {
974
+ // The public function calls the private tail recursive function
975
+ factorial_loop(x, 1)
976
+ }
977
+
978
+ fn factorial_loop(x: Int, accumulator: Int) -> Int {
979
+ case x {
980
+ 0 -> accumulator
981
+ 1 -> accumulator
982
+
983
+ // The last thing this function does is call itself
984
+ // In the previous lesson the last thing it did was multiply two ints
985
+ _ -> factorial_loop(x - 1, accumulator * x)
986
+ }
987
+ }
988
+ ```
989
+
990
+ ---
991
+
992
+ ## List recursion
993
+
994
+ While it is more common to use functions in the [`gleam/list`](https://hexdocs.pm/gleam_stdlib/gleam/list.html) module to iterate across a list, at times you may prefer to work with the list directly.
995
+
996
+ The `[first, ..rest]` pattern matches on a list with at least one element, assigning the first element to the variable `first` and the rest of the list to the variable `rest`. By using this pattern and a pattern for the empty list `[]` a function can run code on each element of a list until the end is reached.
997
+
998
+ This code sums a list by recursing over the list and adding each int to a `total` argument, returning it when the end is reached.
999
+
1000
+ ```gleam
1001
+ pub fn main() {
1002
+ let sum = sum_list([18, 56, 35, 85, 91], 0)
1003
+ echo sum
1004
+ }
1005
+
1006
+ fn sum_list(list: List(Int), total: Int) -> Int {
1007
+ case list {
1008
+ [first, ..rest] -> sum_list(rest, total + first)
1009
+ [] -> total
1010
+ }
1011
+ }
1012
+ ```
1013
+
1014
+ ---
1015
+
1016
+ ## Multiple subjects
1017
+
1018
+ Sometimes it is useful to pattern match on multiple values at the same time in one case expression.
1019
+
1020
+ To do this, you can give multiple subjects and multiple patterns, separated by commas.
1021
+
1022
+ When matching on multiple subjects there must be the same number of patterns as there are subjects. Try removing one of the `_,` sub-patterns to see the compile time error that is returned.
1023
+
1024
+ ```gleam
1025
+ import gleam/int
1026
+
1027
+ pub fn main() {
1028
+ let x = int.random(2)
1029
+ let y = int.random(2)
1030
+ echo x
1031
+ echo y
1032
+
1033
+ let result = case x, y {
1034
+ 0, 0 -> "Both are zero"
1035
+ 0, _ -> "First is zero"
1036
+ _, 0 -> "Second is zero"
1037
+ _, _ -> "Neither are zero"
1038
+ }
1039
+ echo result
1040
+ }
1041
+ ```
1042
+
1043
+ ---
1044
+
1045
+ ## Alternative patterns
1046
+
1047
+ Alternative patterns can be given for a case clause using the `|` operator. If any of the patterns match then the clause matches.
1048
+
1049
+ If a pattern defines a variable then all of the alternative patterns for that clause must also define a variable with the same name and same type.
1050
+
1051
+ Currently it is not possible to have nested alternative patterns, so the pattern `[1 | 2 | 3]` is not valid.
1052
+
1053
+ ```gleam
1054
+ import gleam/int
1055
+
1056
+ pub fn main() {
1057
+ let number = int.random(10)
1058
+ echo number
1059
+
1060
+ let result = case number {
1061
+ 2 | 4 | 6 | 8 -> "This is an even number"
1062
+ 1 | 3 | 5 | 7 -> "This is an odd number"
1063
+ _ -> "I'm not sure"
1064
+ }
1065
+ echo result
1066
+ }
1067
+ ```
1068
+
1069
+ ---
1070
+
1071
+ ## Pattern aliases
1072
+
1073
+ The `as` operator can be used to assign sub patterns to variables.
1074
+
1075
+ The pattern `[_, ..] as first` will match any non-empty list and assign that list to the variable `first`.
1076
+
1077
+ ```gleam
1078
+ pub fn main() {
1079
+ echo get_first_non_empty([[], [1, 2, 3], [4, 5]])
1080
+ echo get_first_non_empty([[1, 2], [3, 4, 5], []])
1081
+ echo get_first_non_empty([[], [], []])
1082
+ }
1083
+
1084
+ fn get_first_non_empty(lists: List(List(t))) -> List(t) {
1085
+ case lists {
1086
+ [[_, ..] as first, ..] -> first
1087
+ [_, ..rest] -> get_first_non_empty(rest)
1088
+ [] -> []
1089
+ }
1090
+ }
1091
+ ```
1092
+
1093
+ ---
1094
+
1095
+ ## Guards
1096
+
1097
+ The `if` keyword can be used with case expressions to add a _guard_ to a pattern. A guard is an expression that must evaluate to `True` for the pattern to match.
1098
+
1099
+ Guard expressions _cannot_ contain function calls, case expressions, or blocks.
1100
+
1101
+ ```gleam
1102
+ pub fn main() {
1103
+ let numbers = [1, 2, 3, 4, 5]
1104
+ echo get_first_larger(numbers, 3)
1105
+ echo get_first_larger(numbers, 5)
1106
+ }
1107
+
1108
+ fn get_first_larger(numbers: List(Int), limit: Int) -> Int {
1109
+ case numbers {
1110
+ [first, ..] if first > limit -> first
1111
+ [_, ..rest] -> get_first_larger(rest, limit)
1112
+ [] -> 0
1113
+ }
1114
+ }
1115
+ ```
1116
+
1117
+ ---
1118
+
1119
+ ## Tuples
1120
+
1121
+ Lists are good for when we want a collection of one type, but sometimes we want to combine multiple values of different types. In this case tuples are a quick and convenient option.
1122
+
1123
+ The tuple access syntax can be used to get elements from a tuple without pattern matching. `some_tuple.0` gets the first element, `some_tuple.1` gets the second element, etc.
1124
+
1125
+ Tuples are generic types, they have type parameters for the types they contain. `#(1, "Hi!")` has the type `#(Int, String)`, and `#(1.4, 10, 48)` has the type `#(Float, Int, Int)`.
1126
+
1127
+ Tuples are most commonly used to return 2 or 3 values from a function. Often it is clearer to use a _custom type_ where a tuple could be used. We will cover custom types next.
1128
+
1129
+ ```gleam
1130
+ pub fn main() {
1131
+ let triple = #(1, 2.2, "three")
1132
+ echo triple
1133
+
1134
+ let #(a, _, _) = triple
1135
+ echo a
1136
+ echo triple.1
1137
+ }
1138
+ ```
1139
+
1140
+ ---
1141
+
1142
+ ## Custom types
1143
+
1144
+ Gleam has a few built in types such as `Int` and `String`, but custom types allow the creation of entirely new types.
1145
+
1146
+ A custom type is defined with the `type` keyword followed by the name of the type and a constructor for each _variant_ of the type. Both the type name and the names of the constructors start with uppercase letters.
1147
+
1148
+ Custom type variants can be pattern matched on using a case expression.
1149
+
1150
+ ```gleam
1151
+ pub type Season {
1152
+ Spring
1153
+ Summer
1154
+ Autumn
1155
+ Winter
1156
+ }
1157
+
1158
+ pub fn main() {
1159
+ echo weather(Spring)
1160
+ echo weather(Autumn)
1161
+ }
1162
+
1163
+ fn weather(season: Season) -> String {
1164
+ case season {
1165
+ Spring -> "Mild"
1166
+ Summer -> "Hot"
1167
+ Autumn -> "Windy"
1168
+ Winter -> "Cold"
1169
+ }
1170
+ }
1171
+ ```
1172
+
1173
+ ---
1174
+
1175
+ ## Records
1176
+
1177
+ A variant of a custom type can hold other data within it. In this case the variant is called a record.
1178
+
1179
+ The fields of a record can be given labels, and like function argument labels they can be optionally used when calling the record constructor. Typically labels will be used for variants that define them.
1180
+
1181
+ It is common to have a custom type with one variant that holds data, this is the Gleam equivalent of a struct or object in other languages.
1182
+
1183
+ When defining custom types with one variant, the single variant is often named the same as the custom type, although it doesn't have to be.
1184
+
1185
+ ```gleam
1186
+ pub type Person {
1187
+ Person(name: String, age: Int, needs_glasses: Bool)
1188
+ }
1189
+
1190
+ pub fn main() {
1191
+ let amy = Person("Amy", 26, True)
1192
+ let jared = Person(name: "Jared", age: 31, needs_glasses: True)
1193
+ let tom = Person("Tom", 28, needs_glasses: False)
1194
+
1195
+ let friends = [amy, jared, tom]
1196
+ echo friends
1197
+ }
1198
+ ```
1199
+
1200
+ ---
1201
+
1202
+ ## Record accessors
1203
+
1204
+ The record accessor syntax `record.field_label` can be used to get contained values from a custom type record.
1205
+
1206
+ The accessor syntax can always be used for fields with the same name that are in the same position and have the same type for all variants of the custom type. Other fields can only be accessed when the compiler can tell which variant the value is, such after pattern matching in a `case` expression.
1207
+
1208
+ The `name` field is in the first position and has type `String` for all variants, so it can be accessed.
1209
+
1210
+ The `subject` field is absent on the `Student` variant, so it cannot be used on all values of type `SchoolPerson`. Uncomment the `student.subject` line to see the compile error from trying to use this accessor.
1211
+
1212
+ ```gleam
1213
+ pub type SchoolPerson {
1214
+ Teacher(name: String, subject: String)
1215
+ Student(name: String)
1216
+ }
1217
+
1218
+ pub fn main() {
1219
+ let teacher = Teacher("Mr Schofield", "Physics")
1220
+ let student = Student("Koushiar")
1221
+
1222
+ echo teacher.name
1223
+ echo student.name
1224
+ // echo student.subject
1225
+ }
1226
+ ```
1227
+
1228
+ ---
1229
+
1230
+ ## Record pattern matching
1231
+
1232
+ It is possible to pattern match on a record, this allows for the extraction of multiple field values from a record into distinct variables, similar to matching on a tuple or a list.
1233
+
1234
+ The `let` keyword can only match on single variant custom types, or when the variant is known, such as after pattern matching with a case expression.
1235
+
1236
+ It is possible to use underscore `_` or the `..` syntax to discard fields that are not required.
1237
+
1238
+ ```gleam
1239
+ import gleam/io
1240
+
1241
+ pub type Fish {
1242
+ Starfish(name: String, favourite_colour: String)
1243
+ Jellyfish(name: String, jiggly: Bool)
1244
+ }
1245
+
1246
+ pub type IceCream {
1247
+ IceCream(flavour: String)
1248
+ }
1249
+
1250
+ pub fn main() {
1251
+ handle_fish(Starfish("Lucy", "Pink"))
1252
+ handle_ice_cream(IceCream("strawberry"))
1253
+ }
1254
+
1255
+ fn handle_fish(fish: Fish) {
1256
+ case fish {
1257
+ Starfish(_, favourite_colour) -> io.println(favourite_colour)
1258
+ Jellyfish(name, ..) -> io.println(name)
1259
+ }
1260
+ }
1261
+
1262
+ fn handle_ice_cream(ice_cream: IceCream) {
1263
+ // if the custom type has a single variant you can
1264
+ // destructure it using `let` instead of a case expression!
1265
+ let IceCream(flavour) = ice_cream
1266
+ io.println(flavour)
1267
+ }
1268
+ ```
1269
+
1270
+ ---
1271
+
1272
+ ## Record updates
1273
+
1274
+ The record update syntax can be used to create a new record from an existing one of the same type, but with some fields changed.
1275
+
1276
+ Gleam is an immutable language, so using the record update syntax does not mutate or otherwise change the original record.
1277
+
1278
+ ```gleam
1279
+ pub type SchoolPerson {
1280
+ Teacher(name: String, subject: String, floor: Int, room: Int)
1281
+ }
1282
+
1283
+ pub fn main() {
1284
+ let teacher1 = Teacher(name: "Mr Dodd", subject: "ICT", floor: 2, room: 2)
1285
+
1286
+ // Use the update syntax
1287
+ let teacher2 = Teacher(..teacher1, subject: "PE", room: 6)
1288
+
1289
+ echo teacher1
1290
+ echo teacher2
1291
+ }
1292
+ ```
1293
+
1294
+ ---
1295
+
1296
+ ## Generic custom types
1297
+
1298
+ Like functions, custom types can also be generic, taking contained types as parameters.
1299
+
1300
+ Here a generic `Option` type is defined, which is used to represent a value that is either present or absent. This type is quite useful! The [`gleam/option`](https://hexdocs.pm/gleam_stdlib/gleam/option.html) module defines it so you can use it in your Gleam projects.
1301
+
1302
+ ```gleam
1303
+ pub type Option(inner) {
1304
+ Some(inner)
1305
+ None
1306
+ }
1307
+
1308
+ // An option of string
1309
+ pub const name: Option(String) = Some("Annah")
1310
+
1311
+ // An option of int
1312
+ pub const level: Option(Int) = Some(10)
1313
+ ```
1314
+
1315
+ ---
1316
+
1317
+ ## Nil
1318
+
1319
+ `Nil` is Gleam's unit type. It is a value that is returned by functions that have nothing else to return, as all functions must return something.
1320
+
1321
+ `Nil` is not a valid value of any other types. Therefore, values in Gleam are not nullable. If the type of a value is `Nil` then it is the value `Nil`. If it is some other type then the value is not `Nil`.
1322
+
1323
+ Uncomment the line that assigns `Nil` to a variable with an incompatible type annotation to see the compile time error it produces.
1324
+
1325
+ ```gleam
1326
+ import gleam/io
1327
+
1328
+ pub fn main() {
1329
+ let x = Nil
1330
+ echo x
1331
+
1332
+ // let y: List(String) = Nil
1333
+
1334
+ let result = io.println("Hello!")
1335
+ echo result == Nil
1336
+ }
1337
+ ```
1338
+
1339
+ ---
1340
+
1341
+ ## Results
1342
+
1343
+ Gleam doesn't use exceptions, instead computations that can either succeed or fail return a value of the built-in `Result(value, error)` type. It has two variants:
1344
+
1345
+ * `Ok`, which contains the return value of a successful computation.
1346
+ * `Error`, which contains the reason for a failed computation.
1347
+
1348
+ The type is generic with two type parameters, one for the success value and one for the error. With these the result can hold any type for success and failure.
1349
+
1350
+ Commonly a Gleam program or library will define a custom type with a variant for each possible problem that can arise, along with any error information that would be useful to the programmer.
1351
+
1352
+ This is advantageous over exceptions as you can immediately see what if any errors a function can return, and the compiler will ensure they are handled. No nasty surprises with unexpected exceptions!
1353
+
1354
+ A result value can be handled by pattern matching with a `case` expression, but given how frequently results are returned this can become unwieldy. Gleam code commonly uses the [`gleam/result`](https://hexdocs.pm/gleam_stdlib/gleam/result.html) standard library module and `use` expressions when working with results, both of which will be covered in later chapters.
1355
+
1356
+ ```gleam
1357
+ import gleam/int
1358
+
1359
+ pub fn main() {
1360
+ let _ = echo buy_pastry(10)
1361
+ let _ = echo buy_pastry(8)
1362
+ let _ = echo buy_pastry(5)
1363
+ let _ = echo buy_pastry(3)
1364
+ }
1365
+
1366
+ pub type PurchaseError {
1367
+ NotEnoughMoney(required: Int)
1368
+ NotLuckyEnough
1369
+ }
1370
+
1371
+ fn buy_pastry(money: Int) -> Result(Int, PurchaseError) {
1372
+ case money >= 5 {
1373
+ True ->
1374
+ case int.random(4) == 0 {
1375
+ True -> Error(NotLuckyEnough)
1376
+ False -> Ok(money - 5)
1377
+ }
1378
+ False -> Error(NotEnoughMoney(required: 5))
1379
+ }
1380
+ }
1381
+ ```
1382
+
1383
+ ---
1384
+
1385
+ ## Bit arrays
1386
+
1387
+ Bit arrays represent a sequence of 1s and 0s, and are a convenient syntax for constructing and manipulating binary data.
1388
+
1389
+ Each segment of a bit array can be given options to specify the representation used for that segment.
1390
+
1391
+ * `size`: the size of the segment in \`unit\`s.
1392
+ * `unit`: the number of bits that the `size` value is a multiple of, by default 1.
1393
+ * `bits`: a nested bit array of any size.
1394
+ * `bytes`: a nested byte-aligned bit array.
1395
+ * `float`: a floating point number with a default size of 64 bits.
1396
+ * `int`: an int with a default size of 8 bits.
1397
+ * `big`: big endian.
1398
+ * `little`: little endian.
1399
+ * `native`: the endianness of the processor.
1400
+ * `utf8`: utf8 encoded text.
1401
+ * `utf16`: utf16 encoded text.
1402
+ * `utf32`: utf32 encoded text.
1403
+ * `utf8_codepoint`: a utf8 codepoint.
1404
+ * `utf16_codepoint`: a utf16 codepoint.
1405
+ * `utf32_codepoint`: a utf32 codepoint.
1406
+ * `signed`: a signed number.
1407
+ * `unsigned`: an unsigned number.
1408
+
1409
+ Multiple options can be given to a segment by separating each with a dash: `x:unsigned-little-size(2)`.
1410
+
1411
+ Bit arrays have limited support when compiling to JavaScript, not all options can be used. Full bit array support will be implemented in the future.
1412
+
1413
+ For more information on bit arrays see the [Erlang bit syntax documentation](https://www.erlang.org/doc/programming_examples/bit_syntax.html).
1414
+
1415
+ ```gleam
1416
+ pub fn main() {
1417
+ // 8 bit int. In binary: 00000011
1418
+ echo <<3>>
1419
+ echo <<3>> == <<3:size(8)>>
1420
+
1421
+ // 16 bit int. In binary: 0001100000000011
1422
+ echo <<6147:size(16)>>
1423
+
1424
+ // A bit array of UTF8 data
1425
+ echo <<"Hello, Joe!":utf8>>
1426
+
1427
+ // Concatenation
1428
+ let first = <<4>>
1429
+ let second = <<2>>
1430
+ echo <<first:bits, second:bits>>
1431
+ }
1432
+ ```
1433
+
1434
+ ---
1435
+
1436
+ ## Standard library package
1437
+
1438
+ The Gleam standard library is a regular Gleam package that has been published to the [Hex](https://hex.pm) package repository. You could opt to not use it if you wish, though almost all Gleam projects depend on it.
1439
+
1440
+ All of the modules imported so far in this guide, such as [`gleam/io`](https://hexdocs.pm/gleam_stdlib/gleam/io.html) , are from the standard library.
1441
+
1442
+ All of the documentation for the standard library is available on [HexDocs](https://hexdocs.pm/gleam_stdlib/). We will go over some of the most commonly used modules now.
1443
+
1444
+ ```gleam
1445
+ import gleam/io
1446
+
1447
+ pub fn main() {
1448
+ io.println("Hello, Joe!")
1449
+ io.println("Hello, Mike!")
1450
+ }
1451
+ ```
1452
+
1453
+ ---
1454
+
1455
+ ## List module
1456
+
1457
+ The [`gleam/list`](https://hexdocs.pm/gleam_stdlib/gleam/list.html) standard library module contains functions for working with lists. A Gleam program will likely make heavy use of this module, the various functions serving as different types of loops over lists.
1458
+
1459
+ [`map`](https://hexdocs.pm/gleam_stdlib/gleam/list.html#map) makes a new list by running a function on each element in a list.
1460
+
1461
+ [`filter`](https://hexdocs.pm/gleam_stdlib/gleam/list.html#filter) makes a new list containing only the elements for which a function returns true.
1462
+
1463
+ [`fold`](https://hexdocs.pm/gleam_stdlib/gleam/list.html#fold) combines all the elements in a list into a single value by running a function left-to-right on each element, passing the result of the previous call to the next call.
1464
+
1465
+ [`find`](https://hexdocs.pm/gleam_stdlib/gleam/list.html#find) returns the first element in a list for which a function returns `True`.
1466
+
1467
+ It's worth getting familiar with all the functions in this module when writing Gleam code, you'll be using them a lot!
1468
+
1469
+ ```gleam
1470
+ import gleam/io
1471
+ import gleam/list
1472
+
1473
+ pub fn main() {
1474
+ let ints = [0, 1, 2, 3, 4, 5]
1475
+
1476
+ io.println("=== map ===")
1477
+ echo list.map(ints, fn(x) { x * 2 })
1478
+
1479
+ io.println("=== filter ===")
1480
+ echo list.filter(ints, fn(x) { x % 2 == 0 })
1481
+
1482
+ io.println("=== fold ===")
1483
+ echo list.fold(ints, 0, fn(count, e) { count + e })
1484
+
1485
+ io.println("=== find ===")
1486
+ let _ = echo list.find(ints, fn(x) { x > 3 })
1487
+ echo list.find(ints, fn(x) { x > 13 })
1488
+ }
1489
+ ```
1490
+
1491
+ ---
1492
+
1493
+ ## Result module
1494
+
1495
+ The [`gleam/result`](https://hexdocs.pm/gleam_stdlib/gleam/result.html) standard library module contains functions for working with results. Gleam programs will make heavy use of this module to avoid excessive nested case expressions when calling multiple functions that can fail.
1496
+
1497
+ [`map`](https://hexdocs.pm/gleam_stdlib/gleam/result.html#map) updates a value held within the Ok of a result by calling a given function on it. If the result is an error then the function is not called.
1498
+
1499
+ [`try`](https://hexdocs.pm/gleam_stdlib/gleam/result.html#try) runs a result-returning function on the value held within an Ok of a result. If the result is an error then the function is not called. This is useful for chaining together multiple function calls that can fail, one after the other, stopping at the first error.
1500
+
1501
+ [`unwrap`](https://hexdocs.pm/gleam_stdlib/gleam/result.html#unwrap) extracts the success value from a result, or returning a default value if the result is an error.
1502
+
1503
+ Result functions are often used with pipelines to chain together multiple calls to result-returning functions.
1504
+
1505
+ ```gleam
1506
+ import gleam/int
1507
+ import gleam/io
1508
+ import gleam/result
1509
+
1510
+ pub fn main() {
1511
+ io.println("=== map ===")
1512
+ let _ = echo result.map(Ok(1), fn(x) { x * 2 })
1513
+ let _ = echo result.map(Error(1), fn(x) { x * 2 })
1514
+
1515
+ io.println("=== try ===")
1516
+ let _ = echo result.try(Ok("1"), int.parse)
1517
+ let _ = echo result.try(Ok("no"), int.parse)
1518
+ let _ = echo result.try(Error(Nil), int.parse)
1519
+
1520
+ io.println("=== unwrap ===")
1521
+ echo result.unwrap(Ok("1234"), "default")
1522
+ echo result.unwrap(Error(Nil), "default")
1523
+
1524
+ io.println("=== pipeline ===")
1525
+ int.parse("-1234")
1526
+ |> result.map(int.absolute_value)
1527
+ |> result.try(int.remainder(_, 42))
1528
+ |> echo
1529
+ }
1530
+ ```
1531
+
1532
+ ---
1533
+
1534
+ ## Dict module
1535
+
1536
+ The [`gleam/dict`](https://hexdocs.pm/gleam_stdlib/gleam/dict.html) standard library module defines Gleam's `Dict` type and functions for working with it. A dict is a collection of keys and values which other languages may call a hashmap or table.
1537
+
1538
+ [`new`](https://hexdocs.pm/gleam_stdlib/gleam/dict.html#new) and [`from_list`](https://hexdocs.pm/gleam_stdlib/gleam/dict.html#from_list) can be used to create new dicts.
1539
+
1540
+ [`insert`](https://hexdocs.pm/gleam_stdlib/gleam/dict.html#insert) and [`delete`](https://hexdocs.pm/gleam_stdlib/gleam/dict.html#delete) are used to add and remove items from a dict.
1541
+
1542
+ Like lists, dicts are immutable. Inserting or deleting an item from a dict will return a new dict with the item added or removed.
1543
+
1544
+ Dicts are unordered! If it appears that the items in a dict are in a certain order, it is incidental and should not be relied upon. Any ordering may change without warning in future versions or on different runtimes.
1545
+
1546
+ ```gleam
1547
+ import gleam/dict
1548
+
1549
+ pub fn main() {
1550
+ let scores = dict.from_list([#("Lucy", 13), #("Drew", 15)])
1551
+ echo scores
1552
+
1553
+ let scores =
1554
+ scores
1555
+ |> dict.insert("Bushra", 16)
1556
+ |> dict.insert("Darius", 14)
1557
+ |> dict.delete("Drew")
1558
+ echo scores
1559
+ }
1560
+ ```
1561
+
1562
+ ---
1563
+
1564
+ ## Option module
1565
+
1566
+ Values in Gleam are not nullable, so the [`gleam/option`](https://hexdocs.pm/gleam_stdlib/gleam/option.html) standard library module defines Gleam's [`Option`](https://hexdocs.pm/gleam_stdlib/gleam/option.html#Option) type, which can be used to represent a value that is either present or absent.
1567
+
1568
+ The option type is very similar to the result type, but it does not have an error value. Some languages have functions that return an option when there is no extra error detail to give, but Gleam always uses result. This makes all fallible functions consistent and removes any boilerplate that would be required when mixing functions that use each type.
1569
+
1570
+ ```gleam
1571
+ import gleam/option.{type Option, None, Some}
1572
+
1573
+ pub type Person {
1574
+ Person(name: String, pet: Option(String))
1575
+ }
1576
+
1577
+ pub fn main() {
1578
+ let person_with_pet = Person("Al", Some("Nubi"))
1579
+ let person_without_pet = Person("Maria", None)
1580
+
1581
+ echo person_with_pet
1582
+ echo person_without_pet
1583
+ }
1584
+ ```
1585
+
1586
+ ---
1587
+
1588
+ ## Opaque types
1589
+
1590
+ _Opaque types_ are types where a custom type itself is public and can be used by other modules, but the constructors for the type are private and can only be used by the module that defines the type. This prevents other modules from constructing or pattern matching on the type.
1591
+
1592
+ This is useful for creating types with _smart constructors_. A smart constructor is a function that constructs a value of a type, but is more restrictive than if the programmer were to use one of the type's constructors directly. This can be useful for ensuring that the type is used correctly.
1593
+
1594
+ For example, this `PositiveInt` custom type is opaque. If other modules want to construct one they have to use the `new` function, which ensures that the integer is positive.
1595
+
1596
+ ```gleam
1597
+ pub fn main() {
1598
+ let positive = new(1)
1599
+ let zero = new(0)
1600
+ let negative = new(-1)
1601
+
1602
+ echo to_int(positive)
1603
+ echo to_int(zero)
1604
+ echo to_int(negative)
1605
+ }
1606
+
1607
+ pub opaque type PositiveInt {
1608
+ PositiveInt(inner: Int)
1609
+ }
1610
+
1611
+ pub fn new(i: Int) -> PositiveInt {
1612
+ case i >= 0 {
1613
+ True -> PositiveInt(i)
1614
+ False -> PositiveInt(0)
1615
+ }
1616
+ }
1617
+
1618
+ pub fn to_int(i: PositiveInt) -> Int {
1619
+ i.inner
1620
+ }
1621
+ ```
1622
+
1623
+ ---
1624
+
1625
+ ## Use
1626
+
1627
+ Gleam lacks exceptions, macros, type classes, early returns, and a variety of other features, instead going all-in with just first-class-functions and pattern matching. This makes Gleam code easier to understand, but it can sometimes result in excessive indentation.
1628
+
1629
+ Gleam's `use` expression is for calling functions that take a callback as an argument without increasing the indentation of the code.
1630
+
1631
+ All the code below the `use` becomes an anonymous function that is passed as a final argument to the function on the right hand side of the `<-`, and the assigned variables become the arguments to the anonymous function.
1632
+
1633
+ This code:
1634
+
1635
+ ```
1636
+ pub fn main() -> Nil {
1637
+ use a, b <- my_function
1638
+ next(a)
1639
+ next(b)
1640
+ }
1641
+ ```
1642
+
1643
+ Expands into this code:
1644
+
1645
+ ```
1646
+ pub fn main() -> Nil {
1647
+ my_function(fn(a, b) {
1648
+ next(a)
1649
+ next(b)
1650
+ })
1651
+ }
1652
+ ```
1653
+
1654
+ To ensure that your `use` code works and is as understandable as possible, the right-hand-side ideally should be a regular function call rather than a more complex expression, which would be more difficult to read.
1655
+
1656
+ This is a very capable and useful feature, but excessive application of `use` may result in unclear code, especially to beginners. Usually the regular function call syntax results in more approachable code!
1657
+
1658
+ ```gleam
1659
+ import gleam/result
1660
+
1661
+ pub fn main() {
1662
+ // These two functions do exactly the same thing, but one is written with
1663
+ // `use`, and one without.
1664
+ let _ = echo with_use()
1665
+ let _ = echo without_use()
1666
+ }
1667
+
1668
+ pub fn with_use() -> Result(String, Nil) {
1669
+ use username <- result.try(get_username())
1670
+ use password <- result.try(get_password())
1671
+ use greeting <- result.map(log_in(username, password))
1672
+ greeting <> ", " <> username
1673
+ }
1674
+
1675
+ pub fn without_use() -> Result(String, Nil) {
1676
+ result.try(get_username(), fn(username) {
1677
+ result.try(get_password(), fn(password) {
1678
+ result.map(log_in(username, password), fn(greeting) {
1679
+ greeting <> ", " <> username
1680
+ })
1681
+ })
1682
+ })
1683
+ }
1684
+
1685
+ // Here are some pretend functions for this example:
1686
+
1687
+ fn get_username() -> Result(String, Nil) {
1688
+ Ok("alice")
1689
+ }
1690
+
1691
+ fn get_password() -> Result(String, Nil) {
1692
+ Ok("hunter2")
1693
+ }
1694
+
1695
+ fn log_in(_username: String, _password: String) -> Result(String, Nil) {
1696
+ Ok("Welcome")
1697
+ }
1698
+ ```
1699
+
1700
+ ---
1701
+
1702
+ ## Todo
1703
+
1704
+ The `todo` keyword is used to specify that some code is not yet implemented.
1705
+
1706
+ The `as "some string"` is optional, though you may wish to include the message if you have more than one code block marked as `todo` in your code.
1707
+
1708
+ When used the Gleam compiler will print a warning to remind you the code is unfinished, and if the code is run then the program will crash with the given message.
1709
+
1710
+ ```gleam
1711
+ pub fn main() {
1712
+ todo as "I haven't written this code yet!"
1713
+ }
1714
+
1715
+ pub fn todo_without_reason() {
1716
+ todo
1717
+ }
1718
+ ```
1719
+
1720
+ ---
1721
+
1722
+ ## Panic
1723
+
1724
+ The `panic` keyword is similar to the `todo` keyword, but it is used to crash the program when the program has reached a point that should never be reached.
1725
+
1726
+ This keyword should almost never be used! It may be useful in initial prototypes and scripts, but its use in a library or production application is a sign that the design could be improved. With well designed types the type system can typically be used to make these invalid states unrepresentable.
1727
+
1728
+ ```gleam
1729
+ import gleam/io
1730
+
1731
+ pub fn main() {
1732
+ print_score(10)
1733
+ print_score(100_000)
1734
+ print_score(-1)
1735
+ }
1736
+
1737
+ pub fn print_score(score: Int) {
1738
+ case score {
1739
+ score if score > 1000 -> io.println("High score!")
1740
+ score if score > 0 -> io.println("Still working on it")
1741
+ _ -> panic as "Scores should never be negative!"
1742
+ }
1743
+ }
1744
+ ```
1745
+
1746
+ ---
1747
+
1748
+ ## Let assert
1749
+
1750
+ `let assert` is another way to intentionally crash your Gleam program. It is similar to the `panic` keyword in that it crashes when the program has reached a point that should never be reached.
1751
+
1752
+ `let assert` is similar to `let` in that it is a way to assign values to variables, but it is different in that the pattern can be _partial_. The pattern does not need to match every possible value of the type being assigned.
1753
+
1754
+ Just like `panic` and `todo`, the `as` keyword can be used with `let assert` to supply a custom panic message if the pattern fails to match and the program crashes.
1755
+
1756
+ Like `panic` this feature should be used sparingly, and likely not at all in libraries.
1757
+
1758
+ ```gleam
1759
+ pub fn main() {
1760
+ let a = unsafely_get_first_element([123])
1761
+ echo a
1762
+
1763
+ let b = unsafely_get_first_element([])
1764
+ echo b
1765
+ }
1766
+
1767
+ pub fn unsafely_get_first_element(items: List(a)) -> a {
1768
+ // This will panic if the list is empty.
1769
+ // A regular `let` would not permit this partial pattern
1770
+ let assert [first, ..] = items as "List should not be empty"
1771
+ first
1772
+ }
1773
+ ```
1774
+
1775
+ ---
1776
+
1777
+ ## Bool assert
1778
+
1779
+ Bool `assert` is the final way to cause a panic in Gleam, used for writing test assertions. It is similar to `let assert`, except instead of asserting that a value matches a specific pattern, it asserts that a boolean value evaluates to `True`.
1780
+
1781
+ Like the other panicking keywords, the `as` keyword can be used to add a custom message to an assertion. This will be shown instead of the generic "Assertion failed" message if the value evaluates to `False`.
1782
+
1783
+ Bool `assert` is designed to be used in test code, and should almost never be used in applications or libraries.
1784
+
1785
+ ```gleam
1786
+ pub fn main() {
1787
+ assert add(1, 2) == 3
1788
+
1789
+ assert add(1, 2) < add(1, 3)
1790
+
1791
+ assert add(6, 2) == add(2, 6) as "Addition should be commutative"
1792
+
1793
+ assert add(2, 2) == 5
1794
+ }
1795
+
1796
+ fn add(a: Int, b: Int) -> Int {
1797
+ a + b
1798
+ }
1799
+ ```
1800
+
1801
+ ---
1802
+
1803
+ ## Externals
1804
+
1805
+ Sometimes in our projects we want to use code written in other languages, most commonly Erlang and JavaScript, depending on which runtime is being used. Gleam's _external functions_ and _external types_ allow us to import and use this non-Gleam code.
1806
+
1807
+ An external type is one that has no constructors. Gleam doesn't know what shape it has or how to create one, it only knows that it exists.
1808
+
1809
+ An external function is one that has the `@external` attribute on it, directing the compiler to use the specified module function as the implementation, instead of Gleam code.
1810
+
1811
+ The compiler can't tell the types of functions written in other languages, so when the external attribute is given type annotations must be provided. Gleam trusts that the type given is correct so an inaccurate type annotation can result in unexpected behaviour and crashes at runtime. Be careful!
1812
+
1813
+ External functions are useful but should be used sparingly. Prefer to write Gleam code where possible.
1814
+
1815
+ ```gleam
1816
+ // A type with no Gleam constructors
1817
+ pub type DateTime
1818
+
1819
+ // An external function that creates an instance of the type
1820
+ @external(javascript, "./my_package_ffi.mjs", "now")
1821
+ pub fn now() -> DateTime
1822
+
1823
+ // The `now` function in `./my_package_ffi.mjs` looks like this:
1824
+ // export function now() {
1825
+ // return new Date();
1826
+ // }
1827
+
1828
+ pub fn main() {
1829
+ echo now()
1830
+ }
1831
+ ```
1832
+
1833
+ ---
1834
+
1835
+ ## Multi target externals
1836
+
1837
+ Multiple external implementations can be specified for the same function, enabling the function to work on both Erlang and JavaScript.
1838
+
1839
+ If a function doesn't have an implementation for the currently compiled-for target then the compiler will return an error.
1840
+
1841
+ You should try to implement functions for all targets, but this isn't always possible due to incompatibilities in how IO and concurrency works in Erlang and JavaScript. With Erlang concurrent IO is handled transparently by the runtime, while in JavaScript concurrent IO requires the use of promises or callbacks. If your code uses the Erlang style it is typically not possible to implement in JavaScript, while if callbacks are used then it won't be compatible with most Gleam and Erlang code as it forces any code that calls the function to also use callbacks.
1842
+
1843
+ Libraries that make use of concurrent IO will typically have to decide whether they support Erlang or JavaScript, and document this in their README.
1844
+
1845
+ ```gleam
1846
+ pub type DateTime
1847
+
1848
+ @external(erlang, "calendar", "local_time")
1849
+ @external(javascript, "./my_package_ffi.mjs", "now")
1850
+ pub fn now() -> DateTime
1851
+
1852
+ pub fn main() {
1853
+ echo now()
1854
+ }
1855
+ ```
1856
+
1857
+ ---
1858
+
1859
+ ## External gleam fallbacks
1860
+
1861
+ It's possible for a function to have both a Gleam implementation and an external implementation. If there exists an external implementation for the currently compiled-for target then it will be used, otherwise the Gleam implementation is used.
1862
+
1863
+ This may be useful if you have a function that can be implemented in Gleam, but there is an optimised implementation that can be used for one target. For example, the Erlang virtual machine has a built-in list reverse function that is implemented in native code. The code here uses this implementation when running on Erlang, as it is then available.
1864
+
1865
+ For further information on externals see the [externals guide](https://gleam.run/documentation/externals/) on the Gleam website.
1866
+
1867
+ ```gleam
1868
+ @external(erlang, "lists", "reverse")
1869
+ pub fn reverse_list(items: List(e)) -> List(e) {
1870
+ tail_recursive_reverse(items, [])
1871
+ }
1872
+
1873
+ fn tail_recursive_reverse(items: List(e), reversed: List(e)) -> List(e) {
1874
+ case items {
1875
+ [] -> reversed
1876
+ [first, ..rest] -> tail_recursive_reverse(rest, [first, ..reversed])
1877
+ }
1878
+ }
1879
+
1880
+ pub fn main() {
1881
+ echo reverse_list([1, 2, 3, 4, 5])
1882
+ echo reverse_list(["a", "b", "c", "d", "e"])
1883
+ }
1884
+ ```