summaryrefslogtreecommitdiffhomepage
path: root/gleam.html.markdown
diff options
context:
space:
mode:
authorAntonio Ognio <30294002+aognio@users.noreply.github.com>2024-05-13 01:11:06 -0500
committerGitHub <noreply@github.com>2024-05-13 00:11:06 -0600
commit2c2e8650d9b7b3caba65fa9e3ca6bc55ea08c317 (patch)
treee46e55c742861cfa4e340d0637fae4dd3981d6a0 /gleam.html.markdown
parent519c9566fb4a14836ac7cd26820e10dc97925f10 (diff)
[gleam/en-us] Add initial version of Gleam (#4886)
Diffstat (limited to 'gleam.html.markdown')
-rw-r--r--gleam.html.markdown888
1 files changed, 888 insertions, 0 deletions
diff --git a/gleam.html.markdown b/gleam.html.markdown
new file mode 100644
index 00000000..df6cddfd
--- /dev/null
+++ b/gleam.html.markdown
@@ -0,0 +1,888 @@
+---
+name: Gleam
+category: language
+language: Gleam
+contributors:
+ - ["Antonio Ognio", "https://github.com/aognio/"]
+filename: learngleam.gleam
+---
+
+Gleam is a new language for Erlang's BEAM virtual machine that relies on the
+power of a robust type system, the expressiveness of functional programming,
+and the highly concurrent fault-tolerant Erlang runtime using familiar and
+modern syntax inspired by languages like OCaml, Rust and Elixir.
+
+Being a pretty modern development, Gleam comes with a compiler, a build tool,
+a code formatter, several editor integrations, and package manager.
+
+Being part of the larger BEAM ecosystem, the programs created with Gleam can
+also make use of thousands of published packages written in Erlang or Elixir.
+
+The design of the language is very concise so it feature no null values,
+no exceptions, clear error messages, and a practical type system.
+
+JavaScript is additionally supported as a compile target, so you can run Gleam
+code in browser or any other JS-enabled runtime. When using this feature,
+TypeScript definitions get created, so you can interact with your Gleam code
+confidently, even from the outside.
+
+```gleam
+//// This comment with four slashes is a module-level.
+//// This kind of comments are used to describe the whole module.
+
+import gleam/bool
+import gleam/io
+import gleam/int
+import gleam/float
+import gleam/list
+import gleam/iterator
+import gleam/option.{type Option, None, Some}
+import gleam/result
+import gleam/string
+import gleam/string as text
+
+// A type's name always starts with a capital letter, contrasting to variables
+// and functions, which start with a lowercase letter.
+
+// When the pub keyword is used the type alias is public and can be referred to
+// by other modules.
+
+pub type UserId =
+ Int
+
+pub fn main() {
+ io.println("Hello from learnxinmyminutes.com!")
+ // io.println("This statement got commented out by a two slashes comment.!")
+
+ // Modules are the units in which all Gleam code gets organized.
+ // In a module full will find a bunch of definitions of types, functions, etc.
+ // that seem to belong together.
+ // For example, the gleam/io module contains a variety of functions for
+ // printing, like println.
+
+ // 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 is in a file called io.gleam in a directory called
+ // gleam.
+
+ // 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.
+ // io.println(10)
+ // If you uncomment the previous line you'll get a compile time error reported
+ // as the io.println function only works with strings, not ints.
+
+ // The compile will output an error that looks like this:
+ // error: Type mismatch
+ // ┌─ /home/contributor/learnxinmyminutes/src/learnxinmyminutes.gleam:21:14
+ // │
+ // 21 │ io.println(10)
+ // │ ^^
+ //
+ // Expected type:
+ //
+ // String
+ //
+ // Found type:
+ //
+ // Int
+
+ // Working with numbers
+
+ // 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.
+
+ // Int arithmetic
+ io.debug(1 + 1)
+ io.debug(5 - 1)
+ io.debug(5 / 2)
+ io.debug(3 * 3)
+ io.debug(5 % 2)
+
+ // Int comparisons
+ io.debug(2 > 1)
+ io.debug(2 < 1)
+ io.debug(2 >= 1)
+ io.debug(2 <= 1)
+
+ // Equality works for any type and 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.
+ io.debug(1 == 1)
+ // True
+ io.debug(2 != 2)
+ // False
+
+ // Standard library int functions
+ io.debug(int.min(142, 137))
+ // 137
+ io.debug(int.clamp(-80, min: 0, max: 100))
+ // 0
+ io.debug(int.base_parse("10", 2))
+ // Ok(2)
+
+ // Binary, octal, and hex Int literals
+ io.debug(0b00001111)
+ io.debug(0o17)
+ io.debug(0xF)
+
+ // Use underscores to enhance integer readibility
+ io.debug(1_000_000)
+
+ // Gleam's numerical operators are not overloaded, so there are dedicated
+ // operators for working with floats.
+
+ // Float arithmetic
+ io.debug(1.0 +. 1.5)
+ io.debug(5.0 -. 1.5)
+ io.debug(5.0 /. 2.5)
+ io.debug(3.0 *. 3.5)
+
+ // Float comparisons
+ io.debug(2.2 >. 1.3)
+ io.debug(2.2 <. 1.3)
+ io.debug(2.2 >=. 1.3)
+ io.debug(2.2 <=. 1.3)
+
+ // 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.
+
+ // 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.
+
+ // When running on the BEAM any overflow will raise an error. So there is no
+ // NaN or Infinity float value in the Erlang runtime.
+
+ // Division by zero is not an error
+ io.debug(3.14 /. 0.0)
+ // 0.0
+
+ // Standard library float functions
+ io.debug(float.max(2.0, 9.5))
+ // 9.5
+ io.debug(float.ceiling(5.4))
+ // 6.0
+
+ // Underscores for floats are also supported
+ io.debug(10_000.01)
+
+ // Division by zero will not overflow, but is instead defined to be zero.
+
+ // Working with strings
+ io.debug("⭐ Gleam ⭐ - 별")
+ io.debug(
+ "this
+ is
+ a
+ multi
+ line
+ string",
+ )
+ io.debug("\u{1F600}")
+ // Outputs a smiley 😀
+
+ // Double quote can be escaped
+ io.println("\"X\" marks the spot")
+
+ // String concatenation
+ io.debug("One " <> "Two")
+
+ // String functions
+ io.debug(text.reverse("1 2 3 4 5"))
+ io.debug(text.append("abc", "def"))
+
+ io.println(text.reverse("!desrever tog gnirts sihT"))
+ // Outputs "This string got reversed!"
+
+ // Several escape sequences are supported:
+
+ // \" - double quote
+ // \\ - backslash
+ // \f - form feed
+ // \n - newline
+ // \r - carriage return
+ // \t - tab
+
+ // Bool operators
+ // The || and && operators work by short-circuiting
+
+ io.debug(True && False)
+ // False
+
+ io.debug(True && True)
+ // True
+
+ io.debug(False || False)
+ // False
+
+ io.debug(False || True)
+ // True
+
+ // Bool functions
+ io.debug(bool.to_string(True))
+ // "True"
+
+ io.debug(bool.to_int(False))
+ // 0
+
+ // Assignments
+ let x = "Original value"
+ io.debug(x)
+
+ // Assign `y` to the value of `x`
+ let y = x
+ io.debug(y)
+
+ // Assign `x` to a new value
+ let x = "New value"
+ io.debug(x)
+
+ // The `y` still refers to the original value
+ io.debug(y)
+
+ // In Gleam variable and function names are written in snake_case.
+ let answer_to_the_universe = 42
+ io.debug(answer_to_the_universe)
+
+ let and_everything = answer_to_the_universe
+ // Now using a variable produces a warning
+
+ // warning: Unused variable
+ // ┌─ /home/contributor/learnxinmyminutes/src/learnxinmyminutes.gleam:199:7
+ // │
+ // 199 │ let and_everything = answer_to_the_universe
+ // │ ^^^^^^^^^^^^^^ This variable is never used
+ // Hint: You can ignore it with an underscore: `_and_everything`.
+
+ // Type annotations
+
+ let _name: String = "Gleam"
+
+ let _is_cool: Bool = True
+
+ let _version: Int = 1
+ // Useful for documentation purposes but they do not change how the compiler
+ // type checks the code beyond making sure the annotation matches the type,
+ // otherwise you get an error.
+
+ // let _has_wrong_type_annotation: Int = True
+
+ // error: Type mismatch
+ // ┌─ /home/contributor/learnxinmyminutes/src/learnxinmyminutes.gleam:219:41
+ // │
+ // 219 │ let _has_wrong_type_annotation: Int = True
+ // │ ^^^^
+ //
+ // Expected type:
+ //
+ // Int
+ //
+ // Found type:
+ //
+ // Bool
+
+ // Type aliases
+ let one: UserId = 1
+ // Refer to the beginning of the file for the definition of the UserId type
+
+ let two: Int = 2
+
+ // Aliases are just for creating more readable code and more precise
+ // documentation.
+ // Under the hood they are still values of the same type so operations
+ // still work
+ io.debug(one + two)
+ // 3
+
+ // Blocks: scoping and value
+ let radius = {
+ let value = 100.0
+ value
+ }
+ // io.debug(value) // <- This will not compile because "value" is out of scope
+
+ let area = 3.14159 *. radius *. radius
+ io.debug(area)
+
+ // Use blocks to group operations instead of parenthesis
+ let n1 = { 3 + 2 } * 5
+ let n2 = 3 + { 2 * 5 }
+ io.debug(n1 != n2)
+ // True
+
+ // Lists
+
+ // Nephews of Scrooge McDuck
+ let nephews = ["Huey", "Dewey", "Louie"]
+ io.debug(nephews)
+ // ["Huey", "Dewey", "Louie"]
+
+ // Immutably prepend so the original list is not changed
+ io.debug(["Donald", ..nephews])
+ // ["Donald", "Huey", "Dewey", "Louie"]
+
+ // Some standard library functions for lists
+
+ list.each(nephews, io.println)
+ // Huey
+ // Dewey
+ // Louie
+
+ io.debug(list.drop(nephews, 2))
+ // ["Louie"]
+
+ more_examples()
+ more_function_examples()
+ generic_typing_examples()
+ beloved_pipelines_demo()
+ labels_in_function_calls()
+ showcase_flow_control()
+ more_on_recursion()
+ more_on_pattern_matching()
+ showcase_types()
+ more_on_types()
+ more_on_callbacks()
+ showcase_externals()
+ showcase_panic()
+}
+
+// The fn keyword is used to define new functions.
+fn multiply(a: Int, b: Int) -> Int {
+ // No explicit return
+ // The last expression gets returned
+ a * b
+}
+
+// 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.
+fn double(a: Int) -> Int {
+ multiply(a, 2)
+}
+
+// Only public functions are exported and can be called from outside the module.
+
+// Type annotations are optional for function arguments and return values
+// but are considered good practice for clarity and in order to encourage
+// intentional and thoughtful design.
+
+pub fn is_leap_year(year: Int) -> Bool {
+ { year % 4 == 0 } && { { year % 100 != 0 } || { year % 400 == 0 } }
+}
+
+fn more_examples() {
+ // Debug also returns a value so its output is the return value of
+ // this function
+ io.debug(double(10))
+ // 20
+ io.debug(is_leap_year(2000))
+ // True
+}
+
+// Gleam supports higher order functions:
+// They can be assigned to variables, passed as arguments to other functions
+// or even be returned as values from blocks or other functions
+fn call_func_on_int(func: fn(Int) -> Int, value: Int) -> Int {
+ func(value)
+}
+
+fn more_function_examples() -> Int {
+ io.debug(call_func_on_int(double, 2))
+ // 4
+
+ let square = fn(x: Int) -> Int { x * x }
+ io.debug(square(3))
+ // 9
+
+ // Calling an anonymous function inmediately after defining it
+ io.debug(fn(x: Int) { x + 1 }(1))
+
+ // Closure example
+ let make_adder = fn(n: Int) -> fn(Int) -> Int {
+ fn(argument: Int) -> Int { argument + n }
+ }
+
+ let adder_of_fives = make_adder(5)
+ io.debug(adder_of_fives(10))
+ // 15
+
+ // Anonymous functions can be used interchangeably with named functions.
+ io.debug(call_func_on_int(fn(x: Int) -> Int { x + 100 }, 900))
+ // 1000
+
+ // Let's create a function decorator
+ let twice = fn(wrapped_func: fn(Int) -> Int) -> fn(Int) -> Int {
+ fn(argument: Int) -> Int { wrapped_func(wrapped_func(argument)) }
+ }
+ let quadruple = twice(double)
+ io.debug(quadruple(1))
+
+ let quadruple_2 = fn(a: Int) -> Int { multiply(4, a) }
+ io.debug(quadruple_2(2))
+ // 8
+
+ // A function capture is a shorthand syntax for creating anonymous functions
+ // that take one argument and immediately call another function with that
+ // argument
+ let quadruple_3 = multiply(4, _)
+ io.debug(quadruple_3(4))
+ // 16
+}
+
+// Generic functions are supported using type variables.
+fn generic_twice(func: fn(value) -> value, argument: value) -> value {
+ func(func(argument))
+}
+
+// In generic_twice value was the type variable.
+// In generic_twice_decorator the_type is the type variable.
+// As in any other variable you get to choose the name.
+fn generic_twice_decorator(
+ func: fn(the_type) -> the_type,
+) -> fn(the_type) -> the_type {
+ fn(argument: the_type) -> the_type { func(func(argument)) }
+}
+
+fn generic_typing_examples() {
+ let double_integers = fn(a: Int) -> Int { a * 2 }
+ let double_floats = fn(a: Float) -> Float { a *. 2.0 }
+ io.debug(generic_twice(double_integers, 3))
+ io.debug(generic_twice(double_floats, 3.0))
+
+ let quadruple_integers = generic_twice_decorator(double_integers)
+ let quadruple_floats = generic_twice_decorator(double_floats)
+ io.debug(quadruple_integers(1))
+ // 4
+ io.debug(quadruple_floats(1.0))
+ // 4.0
+}
+
+// Gleam's pipe operator |> takes the result of the expression on its left
+// and passes it as an argument to the function on its right.
+fn beloved_pipelines_demo() {
+ // Let's be honest: you want to use Gleam just for this cool operator, right?
+ ["hello", "world"]
+ |> list.intersperse(" ")
+ |> list.append(["!"])
+ |> string.concat
+ |> string.capitalise
+ |> io.debug
+
+ // Match cleaner than this right?
+ io.debug(
+ string.capitalise(
+ string.concat(
+ list.append(list.intersperse(["hello", "world"], " "), ["!"]),
+ ),
+ ),
+ )
+
+ // Solution to the first problem of Project Euler:
+ // URL: https://projecteuler.net/problem=1
+ // Description: Find the sum of all the multiples of 3 and 5 below 1000.
+ iterator.iterate(1, fn(n) { n + 1 })
+ |> iterator.take(1000 - 1)
+ |> iterator.filter(fn(n) { { n % 3 == 0 } || { n % 5 == 0 } })
+ |> iterator.fold(from: 0, with: fn(acc, element) { element + acc })
+ |> int.to_string
+ |> fn(sum_as_text: String) {
+ "Solution to Project Euler's problem #1: " <> sum_as_text
+ }
+ |> io.debug
+ // Solution to Project Euler's problem #1: 233168
+}
+
+// Labels can be added before each argument
+fn call_func_on_int_with_labels(
+ func passed_func: fn(Int) -> Int,
+ value n: Int,
+) -> Int {
+ passed_func(n)
+}
+
+// The label and the argument can have the same name
+fn add_one(number number: Int) -> Int {
+ number + 1
+}
+
+fn add_two_integers(first n: Int, second m: Int) -> Int {
+ n + m
+}
+
+fn labels_in_function_calls() -> Int {
+ // Since we are labelling the arguments we can switch the order
+ // if we want to
+ io.debug(call_func_on_int_with_labels(value: 8, func: double))
+ io.debug(add_one(number: 1))
+ // 2
+ io.debug(string.contains(does: "theme", contain: "the"))
+ // True
+ // Unlabeled arguments must go first
+ io.debug(add_two_integers(2, second: 2))
+ // 4
+}
+
+fn showcase_flow_control() {
+ // Use case if you want to use pattern-matching in order to
+ // select which code to execute.
+ // Gleam will make sure all possible values are covered
+ // by performing exhaustiveness checks.
+ // Otherwise you get compilation errors.
+ let puppies = ["Bear", "Frisco", "Ranger"]
+ let count = list.length(of: puppies)
+ {
+ "We have "
+ <> int.to_string(count)
+ <> " "
+ <> // The underscore matches with any other value
+ case count {
+ 1 -> "puppy"
+ _ -> "puppies"
+ }
+ }
+ |> io.debug
+
+ // Gleam allows patterns in case expressions to also assign variables.
+ {
+ "Puppy count: "
+ <> case list.length(puppies) {
+ 0 -> "None."
+ 1 -> "Just one."
+ other -> "As many as " <> int.to_string(other) <> " puppies."
+ }
+ }
+ |> io.debug
+
+ // Consider BEAM languages are functional in design and Gleam is no exception
+ // so there are no if, for or while constructs available.
+
+ // Use pattern-matching for conditionals
+ let answer = 42
+ case answer == 42 {
+ True -> {
+ io.debug("This is the answer to the universe.")
+ }
+ False -> {
+ io.debug("This is the answer to something else.")
+ }
+ }
+
+ // Use recursion instead of looping
+ from_one_to_ten(1)
+}
+
+// Recursive function
+fn from_one_to_ten(n: Int) {
+ io.debug(n)
+ case n {
+ 10 -> Nil
+ _ -> from_one_to_ten(n + 1)
+ }
+}
+
+// In order to avoid memory exhaustion due to creating excesive
+// stack frames when calling functions recursively, Gleam supports
+// "tail call optimisation" which means that the compiler can reuse
+// the stack frame for the current function if a function call is
+// the last thing the function does.
+
+pub fn fib(x: Int) -> Int {
+ // The public function calls the private tail recursive function
+ fib_loop(x, 1)
+}
+
+fn fib_loop(x: Int, accumulator: Int) -> Int {
+ case x {
+ 1 -> accumulator
+
+ // The last thing this function does is call itself
+ // In the previous lesson the last thing it did was multiply two ints
+ _ -> fib_loop(x - 1, accumulator + x)
+ }
+}
+
+// Gleam supports pattern-matching the first element and the remainder
+// of a list with the [x, ..y] pattern inside a case expression.
+fn reverse_list(the_list: List(value)) -> List(value) {
+ case the_list {
+ [head, ..tail] -> list.concat([reverse_list(tail), [head]])
+ [] -> []
+ }
+}
+
+fn more_on_recursion() {
+ io.debug(fib(10))
+ // 55
+ io.debug(reverse_list([1, 2, 3]))
+}
+
+fn more_on_pattern_matching() {
+ // When pattern-matching on strings the <> operator match on strings
+ // with a specific prefix and assigns the reminder to a variable
+ io.debug(case "Hello, Lucy" {
+ "Hello, " <> name -> "Grettings for " <> name
+ _ -> "Potentially no greetings"
+ })
+
+ // Alternative patters are supported so the same clause is used
+ // for multiple values
+ let month = 2
+ let year = 2024
+ let number_of_days = case month {
+ 2 ->
+ case is_leap_year(year) {
+ False -> 28
+ True -> 29
+ }
+ 4 | 6 | 9 | 11 -> 30
+ 1 | 3 | 5 | 7 | 8 | 10 | 12 -> 31
+ _ -> 0
+ }
+ io.debug("Number of days: " <> int.to_string(number_of_days))
+ // 29
+
+ // Guards in pattern-matching:
+ // When using the if keyword an expression must evaluate to True
+ // for the pattern to match.
+ let list_starts_with = fn(the_list: List(value), the_value: value) -> Bool {
+ case the_list {
+ [head, ..] if head == the_value -> True
+ _ -> False
+ }
+ }
+ io.debug(list_starts_with([10, 20, 30], 10))
+ // True
+}
+
+pub type Gender {
+ Male
+ Female
+ Other
+}
+
+// Records:
+// - Support variants
+// - Each variant is similar to a struct with fields
+pub type Shape {
+ Rectangle(base: Float, height: Float)
+ Triangle(base: Float, height: Float)
+}
+
+// Records with one variant resemble structs
+pub type Point {
+ Point(x: Float, y: Float)
+}
+
+fn showcase_types() {
+ // Tuples:
+ // - Can mix together elements of different types
+ // - Their type is implicit e.g. #{1, "Hello"} is of type #{Int, String}
+ // - Their elements can be accessed by numeric indexes
+ let tuple_01 = #(1, "Ferris", "rustacean", True)
+ let tuple_02 = #(1, "Lucy", "starfish", True)
+ io.debug(tuple_01)
+ io.debug(tuple_01.0)
+ // 1
+ io.debug(tuple_02.1)
+ // Lucy
+ let #(_, name, species, _) = tuple_01
+ io.debug(name <> " the " <> species)
+
+ // Pattern-matching with tuples including variable assignment
+ case tuple_02 {
+ #(_, name, _, True) -> io.debug(name <> " is a mascot.")
+ #(_, name, _, False) -> io.debug(name <> " is not a mascot.")
+ }
+
+ // Using a custom type with pattern-matching
+ let gender = Other
+ io.debug(case gender {
+ Male -> "Boy"
+ Female -> "Girl"
+ _ -> "Undetermined"
+ })
+
+ // Using records
+ let rectangle_1 = Rectangle(base: 10.0, height: 20.0)
+ io.debug(rectangle_1.height)
+ // 10.3
+
+ let point_1 = Point(x: 3.2, y: 4.3)
+ io.debug(point_1)
+
+ // Updating a record
+ let point_2 = Point(..point_1, y: 5.7)
+ io.debug(point_2)
+
+ // In Gleam, values ar not nullable.
+ // Nil is the only value of its type.
+ let some_var = Nil
+ let result = io.println("Hello!")
+ io.debug(some_var == result)
+ // True
+}
+
+pub type Mineral {
+ Gold
+ Silver
+ Copper
+}
+
+// Generic custom types with contained types as parameters
+pub type Purity(inner_type) {
+ Pure(inner_type)
+ Impure(inner_type)
+}
+
+pub type Beverage {
+ Water
+ Juice
+}
+
+// Existing custom types from the gleam/option and gleam/result modules
+// facilitate working with nullable values and handling potential errors
+pub type Person {
+ Person(name: String, nickname: Option(String))
+}
+
+pub type DiceError {
+ DiceValueOutOfRange
+}
+
+fn checked_dice_value(value: Int) -> Result(Int, DiceError) {
+ case value {
+ 1 | 2 | 3 | 4 | 5 | 6 -> Ok(value)
+ _ -> Error(DiceValueOutOfRange)
+ }
+}
+
+fn double_dice_value(value: Int) -> Result(Int, DiceError) {
+ case value {
+ 1 | 2 | 3 -> Ok(value * 2)
+ _ -> Error(DiceValueOutOfRange)
+ }
+}
+
+fn more_on_types() {
+ let mineral_sample_01: Purity(Mineral) = Pure(Gold)
+ let mineral_sample_02 = Impure(Silver)
+ io.debug(mineral_sample_01)
+ io.debug(mineral_sample_02)
+
+ // A glass can be empty or not
+ let glass_01: Option(Beverage) = Some(Water)
+ let glass_02 = None
+ io.debug(glass_01)
+ io.debug(glass_02)
+
+ // A person can have a nickname or not
+ let person_01 = Person(name: "John", nickname: Some("The Ripper"))
+ let person_02 = Person(name: "Martin", nickname: None)
+ io.debug(person_01)
+ io.debug(person_02)
+
+ // Working with functions that return values of type Result
+ let dice_01 = 5
+ case checked_dice_value(dice_01) {
+ Ok(checked_value) ->
+ io.debug("The value of " <> int.to_string(checked_value) <> " is OK.")
+ Error(DiceValueOutOfRange) ->
+ io.debug("The value of the dice is out of range")
+ }
+
+ // Let's attempt to double the value if the resulting value is still
+ // a number in any of the sides of the dice.
+ // Otherwise, let's put the max value.
+ 2
+ |> checked_dice_value
+ |> result.try(double_dice_value)
+ |> result.unwrap(or: 6)
+ |> io.debug
+}
+
+pub fn throw_dice_as_result() {
+ Ok(int.random(6) + 1)
+}
+
+pub fn sum_dice_values(a: Int, b: Int) {
+ Ok(a + b)
+}
+
+// Betting on first-class functions and pattern-matching
+// can easily lead to tons of indentation
+fn roll_two_dices_without_use() {
+ result.try(throw_dice_as_result(), fn(first_dice) {
+ result.try(throw_dice_as_result(), fn(second_dice) {
+ result.map(sum_dice_values(first_dice, second_dice), fn(sum) { sum })
+ })
+ })
+}
+
+// The use expression still let us write code that uses callbacks
+// but cleans up excessive indentation:
+// - A call to higher order function go the right side of the <- operator
+// - The argument names for the callback function go on the left hand side of
+// the <- operator
+// - All the remaining code in the enclosing {} block becomes the body of the
+// callback function.
+fn roll_two_dices_with_use() {
+ use first_dice <- result.try(throw_dice_as_result())
+ use second_dice <- result.try(throw_dice_as_result())
+ use sum <- result.map(sum_dice_values(first_dice, second_dice))
+ // this is the remaing code in innermost callback function
+ sum
+}
+
+fn more_on_callbacks() {
+ io.debug(roll_two_dices_without_use())
+ io.debug(roll_two_dices_with_use())
+}
+
+pub type DateTime
+
+// External functions must annotate a return type
+@external(erlang, "calendar", "local_time")
+pub fn now() -> DateTime
+
+fn showcase_externals() {
+ io.debug(now())
+ // #(#(2024, 4, 6), #(14, 4, 16))
+}
+
+fn showcase_panic() {
+ // We can deliberately abort execution by using the panic keyword
+ // in order to make our program crash immediately
+ case 3 == 2 {
+ True -> panic as "The equality operator is broken!"
+ False -> "Equality operator works for integers"
+ }
+ // Calling a function that uses the todo keyword also crashes
+ // homework()
+}
+
+pub fn homework() {
+ todo
+}
+```
+
+## Further reading
+
+* [Gleam's official website](https://gleam.run/)
+* [Language tour](https://tour.gleam.run/) - Includes live code editor
+* [Official documentation](https://gleam.run/documentation/)
+* [Gleam's awesome list](https://github.com/gleam-lang/awesome-gleam)
+* [Exercism track for Gleam](https://exercism.org/tracks/gleam)
+
+There official docs have cheatsheets for people familiar with:
+
+* [Elixir](https://gleam.run/cheatsheets/gleam-for-elixir-users)
+* [Elm](https://gleam.run/cheatsheets/gleam-for-elm-users)
+* [Erlang](https://gleam.run/cheatsheets/gleam-for-erlang-users)
+* [PHP](https://gleam.run/cheatsheets/gleam-for-php-users)
+* [Python](https://gleam.run/cheatsheets/gleam-for-python-users)
+* [Rust](https://gleam.run/cheatsheets/gleam-for-python-users)