aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/content/chapter0_basics/lesson00_hello_world/code.gleam5
-rw-r--r--src/content/chapter0_basics/lesson00_hello_world/text.html26
-rw-r--r--src/content/chapter0_basics/lesson01_basics/code.gleam7
-rw-r--r--src/content/chapter0_basics/lesson01_basics/text.html17
-rw-r--r--src/content/chapter0_basics/lesson02_unqualified_imports/code.gleam10
-rw-r--r--src/content/chapter0_basics/lesson02_unqualified_imports/text.html15
-rw-r--r--src/content/chapter0_basics/lesson03_type_checking/code.gleam7
-rw-r--r--src/content/chapter0_basics/lesson03_type_checking/text.html19
-rw-r--r--src/content/chapter0_basics/lesson04_ints/code.gleam25
-rw-r--r--src/content/chapter0_basics/lesson04_ints/text.html17
-rw-r--r--src/content/chapter0_basics/lesson05_floats/code.gleam24
-rw-r--r--src/content/chapter0_basics/lesson05_floats/text.html19
-rw-r--r--src/content/chapter0_basics/lesson06_number_formats/code.gleam16
-rw-r--r--src/content/chapter0_basics/lesson06_number_formats/text.html13
-rw-r--r--src/content/chapter0_basics/lesson07_strings/code.gleam20
-rw-r--r--src/content/chapter0_basics/lesson07_strings/text.html23
-rw-r--r--src/content/chapter0_basics/lesson08_bools/code.gleam14
-rw-r--r--src/content/chapter0_basics/lesson08_bools/text.html17
-rw-r--r--src/content/chapter0_basics/lesson09_assignments/code.gleam17
-rw-r--r--src/content/chapter0_basics/lesson09_assignments/text.html8
-rw-r--r--src/content/chapter0_basics/lesson10_discard_patterns/code.gleam4
-rw-r--r--src/content/chapter0_basics/lesson10_discard_patterns/text.html10
-rw-r--r--src/content/chapter0_basics/lesson11_type_annotations/code.gleam7
-rw-r--r--src/content/chapter0_basics/lesson11_type_annotations/text.html15
-rw-r--r--src/content/chapter0_basics/lesson12_blocks/code.gleam13
-rw-r--r--src/content/chapter0_basics/lesson12_blocks/text.html23
-rw-r--r--src/content/chapter0_basics/lesson13_lists/code.gleam16
-rw-r--r--src/content/chapter0_basics/lesson13_lists/text.html19
-rw-r--r--src/content/chapter0_basics/lesson14_list_functions/code.gleam15
-rw-r--r--src/content/chapter0_basics/lesson14_list_functions/text.html25
-rw-r--r--src/content/chapter1_functions/lesson00_functions/code.gleam13
-rw-r--r--src/content/chapter1_functions/lesson00_functions/text.html14
-rw-r--r--src/content/chapter1_functions/lesson01_higher_order_functions/code.gleam18
-rw-r--r--src/content/chapter1_functions/lesson01_higher_order_functions/text.html12
-rw-r--r--src/content/chapter1_functions/lesson02_anonymous_functions/code.gleam14
-rw-r--r--src/content/chapter1_functions/lesson02_anonymous_functions/text.html7
-rw-r--r--src/content/chapter1_functions/lesson03_function_captures/code.gleam14
-rw-r--r--src/content/chapter1_functions/lesson03_function_captures/text.html11
-rw-r--r--src/content/chapter1_functions/lesson04_generic_functions/code.gleam19
-rw-r--r--src/content/chapter1_functions/lesson04_generic_functions/text.html25
-rw-r--r--src/content/chapter1_functions/lesson05_pipelines/code.gleam19
-rw-r--r--src/content/chapter1_functions/lesson05_pipelines/text.html25
-rw-r--r--src/content/chapter1_functions/lesson06_labelled_arguments/code.gleam16
-rw-r--r--src/content/chapter1_functions/lesson06_labelled_arguments/text.html23
-rw-r--r--src/content/chapter1_functions/lesson099_documentation_comments/code.gleam19
-rw-r--r--src/content/chapter1_functions/lesson099_documentation_comments/text.html16
-rw-r--r--src/content/chapter2_flow_control/lesson01_case_expressions/code.gleam10
-rw-r--r--src/content/chapter2_flow_control/lesson01_case_expressions/text.html7
-rw-r--r--src/content/chapter2_flow_control/lesson02_variable_patterns/code.gleam16
-rw-r--r--src/content/chapter2_flow_control/lesson02_variable_patterns/text.html17
-rw-r--r--src/content/chapter2_flow_control/lesson03_string_patterns/code.gleam14
-rw-r--r--src/content/chapter2_flow_control/lesson03_string_patterns/text.html9
-rw-r--r--src/content/chapter2_flow_control/lesson04_list_patterns/code.gleam17
-rw-r--r--src/content/chapter2_flow_control/lesson04_list_patterns/text.html15
-rw-r--r--src/content/chapter2_flow_control/lesson05_list_recursion/code.gleam13
-rw-r--r--src/content/chapter2_flow_control/lesson05_list_recursion/text.html22
-rw-r--r--src/content/chapter2_flow_control/lesson06_multiple_subjects/code.gleam17
-rw-r--r--src/content/chapter2_flow_control/lesson06_multiple_subjects/text.html13
-rw-r--r--src/content/chapter2_flow_control/lesson07_alternative_patterns/code.gleam14
-rw-r--r--src/content/chapter2_flow_control/lesson07_alternative_patterns/text.html17
-rw-r--r--src/content/chapter2_flow_control/lesson08_pattern_aliases/code.gleam15
-rw-r--r--src/content/chapter2_flow_control/lesson08_pattern_aliases/text.html7
-rw-r--r--src/content/chapter3_data_types/lesson00_tuples/code.gleam10
-rw-r--r--src/content/chapter3_data_types/lesson00_tuples/text.html20
-rw-r--r--src/content/chapter3_data_types/lesson01_custom_types/code.gleam22
-rw-r--r--src/content/chapter3_data_types/lesson01_custom_types/text.html9
-rw-r--r--src/content/chapter3_data_types/lesson02_records/code.gleam17
-rw-r--r--src/content/chapter3_data_types/lesson02_records/text.html10
-rw-r--r--src/content/chapter3_data_types/lesson03_record_accessors/code.gleam15
-rw-r--r--src/content/chapter3_data_types/lesson03_record_accessors/text.html18
-rw-r--r--src/content/chapter3_data_types/lesson04_record_updates/code.gleam15
-rw-r--r--src/content/chapter3_data_types/lesson04_record_updates/text.html12
-rw-r--r--src/content/chapter3_data_types/lesson05_nil/code.gleam11
-rw-r--r--src/content/chapter3_data_types/lesson05_nil/text.html15
-rw-r--r--src/content/chapter3_data_types/lesson06_bit_arrays/code.gleam13
-rw-r--r--src/content/chapter3_data_types/lesson06_bit_arrays/text.html26
-rw-r--r--src/try_gleam.gleam238
77 files changed, 1316 insertions, 89 deletions
diff --git a/src/content/chapter0_basics/lesson00_hello_world/code.gleam b/src/content/chapter0_basics/lesson00_hello_world/code.gleam
new file mode 100644
index 0000000..30530b2
--- /dev/null
+++ b/src/content/chapter0_basics/lesson00_hello_world/code.gleam
@@ -0,0 +1,5 @@
+import gleam/io
+
+pub fn main() {
+ io.println("Hello, Joe!")
+}
diff --git a/src/content/chapter0_basics/lesson00_hello_world/text.html b/src/content/chapter0_basics/lesson00_hello_world/text.html
new file mode 100644
index 0000000..cb41be9
--- /dev/null
+++ b/src/content/chapter0_basics/lesson00_hello_world/text.html
@@ -0,0 +1,26 @@
+<h2>Hello, friend ๐Ÿ’ซ</h2>
+<p>
+ Welcome to Try Gleam! An interactive tour of the Gleam programming language.
+</p>
+<p>
+ It covers all aspects of the Gleam language, and assuming you have some
+ prior programming experience should teach you everything you need to write
+ real programs in Gleam.
+</p>
+<p>
+ The tour is interactive! The code shown is editable and will be compiled and
+ evaluated as you type. Anything you print using <code>io.println</code> or
+ <code>io.debug</code> will be shown in the bottom section, along with any
+ compile errors and warnings. To evaluate Gleam code the tour compiles Gleam to
+ JavaScript and runs it, all entirely within your browser window.
+</p>
+<p>
+ If at any point you get stuck or have a question do not hesitate to ask in
+ <a href="https://discord.gg/Fm8Pwmy">the Gleam Discord server</a>. We're here
+ to help, and if you find something confusing then it's likely others will too,
+ and we want to know about it so we can improve the tour.
+</p>
+<p>
+ OK, let's go. Click "Next" to get started, or click "Index" to jump to a
+ specific topic.
+</p>
diff --git a/src/content/chapter0_basics/lesson01_basics/code.gleam b/src/content/chapter0_basics/lesson01_basics/code.gleam
new file mode 100644
index 0000000..67cc6b4
--- /dev/null
+++ b/src/content/chapter0_basics/lesson01_basics/code.gleam
@@ -0,0 +1,7 @@
+// Import a Gleam module from the standard library
+import gleam/io
+
+pub fn main() {
+ // Print to the console
+ io.println("Hello, Joe!")
+}
diff --git a/src/content/chapter0_basics/lesson01_basics/text.html b/src/content/chapter0_basics/lesson01_basics/text.html
new file mode 100644
index 0000000..8e2033a
--- /dev/null
+++ b/src/content/chapter0_basics/lesson01_basics/text.html
@@ -0,0 +1,17 @@
+<p>
+ Here is a program that prints out the text "Hello, Joe!".
+</p>
+<p>
+ It does this by using the `println` function which has been imported from the
+ <a href="https://hexdocs.pm/gleam_stdlib/gleam/io.html"><code>gleam/io</code></a>
+ module, which is part of the Gleam standard library.
+</p>
+<p>
+ In a normal Gleam program this program would be run use the command
+ <code>gleam run</code> on the command line, but here in this tutorial the
+ program is automatically compiled and run as the code is edited.
+</p>
+<p>
+ Try changing the text being printed to <code>Hello, Mike!</code> and see what
+ happens.
+</p>
diff --git a/src/content/chapter0_basics/lesson02_unqualified_imports/code.gleam b/src/content/chapter0_basics/lesson02_unqualified_imports/code.gleam
new file mode 100644
index 0000000..2708f25
--- /dev/null
+++ b/src/content/chapter0_basics/lesson02_unqualified_imports/code.gleam
@@ -0,0 +1,10 @@
+// Import the module and one of its functions
+import gleam/io.{println}
+
+pub fn main() {
+ // Use the function in a qualified fashion
+ io.println("This is qualified")
+
+ // Or an unqualified fashion
+ println("This is unqualified")
+}
diff --git a/src/content/chapter0_basics/lesson02_unqualified_imports/text.html b/src/content/chapter0_basics/lesson02_unqualified_imports/text.html
new file mode 100644
index 0000000..8fda45e
--- /dev/null
+++ b/src/content/chapter0_basics/lesson02_unqualified_imports/text.html
@@ -0,0 +1,15 @@
+<p>
+ Normally functions from other modules are used in a qualified fashion, with
+ the module qualifier before function name. For example,
+ <code>io.println("Hello!")</code>.
+</p>
+<p>
+ It is also possible to specify a list of functions to import from a module in
+ an unqualified fashion, such as the <code>println</code> function in the code
+ editor. Because it has been imported like this it can be referred to as just
+ <code>println</code>.
+</p>
+<p>
+ Generally it is best to use qualified imports, as this makes it clear where
+ the function is defined, making the code easier to read.
+</p>
diff --git a/src/content/chapter0_basics/lesson03_type_checking/code.gleam b/src/content/chapter0_basics/lesson03_type_checking/code.gleam
new file mode 100644
index 0000000..e068f31
--- /dev/null
+++ b/src/content/chapter0_basics/lesson03_type_checking/code.gleam
@@ -0,0 +1,7 @@
+import gleam/io
+
+pub fn main() {
+ io.println("My lucky number is:")
+ // io.println(4)
+ // ๐Ÿ‘†๏ธ Uncomment this line
+}
diff --git a/src/content/chapter0_basics/lesson03_type_checking/text.html b/src/content/chapter0_basics/lesson03_type_checking/text.html
new file mode 100644
index 0000000..fadfe64
--- /dev/null
+++ b/src/content/chapter0_basics/lesson03_type_checking/text.html
@@ -0,0 +1,19 @@
+<p>
+ Gleam has a robust static type system that is help you as you write and edit
+ code, catching mistakes and showing you where to make changes.
+</p>
+<p>
+ Uncomment the line <code>io.println(4)</code> and see how a compile time error
+ is reported as the <code>io.println</code> function only works with strings,
+ not ints.
+</p>
+<p>
+ To fix the code change the code to call the <code>io.debug</code>
+ function instead, as it will print a value of any type.
+</p>
+<p>
+ Gleam has no <code>null</code>, 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.
+</p>
diff --git a/src/content/chapter0_basics/lesson04_ints/code.gleam b/src/content/chapter0_basics/lesson04_ints/code.gleam
new file mode 100644
index 0000000..cb7991b
--- /dev/null
+++ b/src/content/chapter0_basics/lesson04_ints/code.gleam
@@ -0,0 +1,25 @@
+import gleam/io
+import gleam/int
+
+pub fn main() {
+ // 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
+ io.debug(1 == 1)
+ io.debug(2 == 1)
+
+ // Standard library int functions
+ io.debug(int.max(42, 77))
+ io.debug(int.clamp(5, 10, 20))
+}
diff --git a/src/content/chapter0_basics/lesson04_ints/text.html b/src/content/chapter0_basics/lesson04_ints/text.html
new file mode 100644
index 0000000..252496a
--- /dev/null
+++ b/src/content/chapter0_basics/lesson04_ints/text.html
@@ -0,0 +1,17 @@
+<p>Gleam's <code>Int</code> type represents whole numbers.</p>
+<p>
+ There are arithmetic and comparison operators for ints, as well as the
+ equality operator which works on all types.
+</p>
+<p>
+ 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,
+</p>
+<p>
+ The
+ <a href="https://hexdocs.pm/gleam_stdlib/gleam/int.html"
+ ><code>gleam/int</code></a
+ >
+ standard library module contains functions for working with ints.
+</p>
diff --git a/src/content/chapter0_basics/lesson05_floats/code.gleam b/src/content/chapter0_basics/lesson05_floats/code.gleam
new file mode 100644
index 0000000..8c4e89a
--- /dev/null
+++ b/src/content/chapter0_basics/lesson05_floats/code.gleam
@@ -0,0 +1,24 @@
+import gleam/io
+import gleam/float
+
+pub fn main() {
+ // 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)
+
+ // Equality works for any type
+ io.debug(1.1 == 1.1)
+ io.debug(2.1 == 1.2)
+
+ // Standard library float functions
+ io.debug(float.max(2.0, 9.5))
+ io.debug(float.ceiling(5.4))
+}
diff --git a/src/content/chapter0_basics/lesson05_floats/text.html b/src/content/chapter0_basics/lesson05_floats/text.html
new file mode 100644
index 0000000..497bb13
--- /dev/null
+++ b/src/content/chapter0_basics/lesson05_floats/text.html
@@ -0,0 +1,19 @@
+<p>
+ Gleam's <code>Float</code> type represents numbers that are not integers.
+</p>
+<p>
+ Unlike many languages Gleam does not have a `NaN` or `Infinity` float value.
+</p>
+<p>
+ Gleam's numerical operators are not overloaded, so there are dedictated
+ operators for working with floats.
+</p>
+<p>
+ Floats are represented as 64 bit floating point numbers on both Erlang and
+ JavaScript runtimes.
+</p>
+<p>
+ The <a href="https://hexdocs.pm/gleam_stdlib/gleam/float.html"><code>gleam/float</code></a>
+ standard library module contains functions for working with floats.
+</p>
+
diff --git a/src/content/chapter0_basics/lesson06_number_formats/code.gleam b/src/content/chapter0_basics/lesson06_number_formats/code.gleam
new file mode 100644
index 0000000..7307185
--- /dev/null
+++ b/src/content/chapter0_basics/lesson06_number_formats/code.gleam
@@ -0,0 +1,16 @@
+import gleam/io
+
+pub fn main() {
+ // Underscores
+ io.debug(1_000_000)
+ io.debug(10_000.01)
+
+ // Binary, octal, and hex Int literals
+ io.debug(0b00001111)
+ io.debug(0o17)
+ io.debug(0xF)
+
+ // Scientific notation Float literals
+ io.debug(7.0e7)
+ io.debug(3.0e-4)
+}
diff --git a/src/content/chapter0_basics/lesson06_number_formats/text.html b/src/content/chapter0_basics/lesson06_number_formats/text.html
new file mode 100644
index 0000000..308219a
--- /dev/null
+++ b/src/content/chapter0_basics/lesson06_number_formats/text.html
@@ -0,0 +1,13 @@
+<p>
+ Underscores can be added to numbers for clarity. For example,
+ <code>1000000</code> can be tricky to read quickly, while
+ <code>1_000_000</code> can be easier.
+</p>
+<p>
+ Ints can be written in binary, octal, or hexadecimal formats using the
+ <code>0b</code>, <code>0o</code>, and <code>0x</code> prefixes respectively.
+</p>
+<p>
+ Floats can be written in a scientific notation.
+</p>
+
diff --git a/src/content/chapter0_basics/lesson07_strings/code.gleam b/src/content/chapter0_basics/lesson07_strings/code.gleam
new file mode 100644
index 0000000..c77163e
--- /dev/null
+++ b/src/content/chapter0_basics/lesson07_strings/code.gleam
@@ -0,0 +1,20 @@
+import gleam/io
+import gleam/string
+
+pub fn main() {
+ // String literals
+ io.debug("๐Ÿ‘ฉโ€๐Ÿ’ป ใ“ใ‚“ใซใกใฏ Gleam ๐Ÿณ๏ธโ€๐ŸŒˆ")
+ io.debug(
+ "multi
+ line
+ string",
+ )
+ io.debug("\u{1F600}")
+
+ // String concatenation
+ io.debug("One " <> "Two")
+
+ // String functions
+ io.debug(string.reverse("1 2 3 4 5"))
+ io.debug(string.append("abc", "def"))
+}
diff --git a/src/content/chapter0_basics/lesson07_strings/text.html b/src/content/chapter0_basics/lesson07_strings/text.html
new file mode 100644
index 0000000..820f1b3
--- /dev/null
+++ b/src/content/chapter0_basics/lesson07_strings/text.html
@@ -0,0 +1,23 @@
+<p>
+ In Gleam Strings are written as text surrounded by double quotes, and
+ can span multiple lines and contain unicode characters.
+</p>
+<p>
+ The <code>&lt;&gt;</code> operator can be used to concatenate strings.
+</p>
+<p>
+ Several escape sequences are supported:
+</p>
+<ul>
+ <li><code>\"</code> - double quote</li>
+ <li><code>\\</code> - backslash</li>
+ <li><code>\f</code> - form feed</li>
+ <li><code>\n</code> - newline</li>
+ <li><code>\r</code> - carriage return</li>
+ <li><code>\t</code> - tab</li>
+ <li><code>\u{xxxxxx}</code> - unicode codepoint</li>
+</ul>
+<p>
+ The <a href="https://hexdocs.pm/gleam_stdlib/gleam/string.html"><code>gleam/string</code></a>
+ standard library module contains functions for working with strings.
+</p>
diff --git a/src/content/chapter0_basics/lesson08_bools/code.gleam b/src/content/chapter0_basics/lesson08_bools/code.gleam
new file mode 100644
index 0000000..e5c1d98
--- /dev/null
+++ b/src/content/chapter0_basics/lesson08_bools/code.gleam
@@ -0,0 +1,14 @@
+import gleam/io
+import gleam/bool
+
+pub fn main() {
+ // Bool operators
+ io.debug(True && False)
+ io.debug(True && True)
+ io.debug(False || False)
+ io.debug(False || True)
+
+ // Bool functions
+ io.debug(bool.to_string(True))
+ io.debug(bool.to_int(False))
+}
diff --git a/src/content/chapter0_basics/lesson08_bools/text.html b/src/content/chapter0_basics/lesson08_bools/text.html
new file mode 100644
index 0000000..3f60743
--- /dev/null
+++ b/src/content/chapter0_basics/lesson08_bools/text.html
@@ -0,0 +1,17 @@
+<p>
+ A <code>Bool</code> is a either <code>True</code> or <code>False</code>.
+</p>
+<p>
+ The <code>||</code>, <code>&&</code>, and <code>!</code> operators can be used
+ to manipulate bools.
+</p>
+<p>
+ The <code>||</code> and <code>&&</code> operators are short-circuiting,
+ meaning that if the left hand side of the operator is <code>True</code> for
+ <code>||</code> or <code>False</code> for <code>&&</code> then the right hand
+ side of the operator will not be evaluated.
+</p>
+<p>
+ The <a href="https://hexdocs.pm/gleam_stdlib/gleam/bool.html"><code>gleam/bool</code></a>
+ standard library module contains functions for working with bools.
+</p>
diff --git a/src/content/chapter0_basics/lesson09_assignments/code.gleam b/src/content/chapter0_basics/lesson09_assignments/code.gleam
new file mode 100644
index 0000000..a030e43
--- /dev/null
+++ b/src/content/chapter0_basics/lesson09_assignments/code.gleam
@@ -0,0 +1,17 @@
+import gleam/io
+
+pub fn main() {
+ let x = "Original"
+ 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"
+ io.debug(x)
+
+ // The `y` still refers to the original value
+ io.debug(y)
+}
diff --git a/src/content/chapter0_basics/lesson09_assignments/text.html b/src/content/chapter0_basics/lesson09_assignments/text.html
new file mode 100644
index 0000000..6d535de
--- /dev/null
+++ b/src/content/chapter0_basics/lesson09_assignments/text.html
@@ -0,0 +1,8 @@
+<p>
+ A value can be assigned to a variable using <code>let</code>.
+</p>
+<p>
+ 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.
+</p>
diff --git a/src/content/chapter0_basics/lesson10_discard_patterns/code.gleam b/src/content/chapter0_basics/lesson10_discard_patterns/code.gleam
new file mode 100644
index 0000000..fa2c0e3
--- /dev/null
+++ b/src/content/chapter0_basics/lesson10_discard_patterns/code.gleam
@@ -0,0 +1,4 @@
+pub fn main() {
+ // This variable is never used
+ let _score = 1000
+}
diff --git a/src/content/chapter0_basics/lesson10_discard_patterns/text.html b/src/content/chapter0_basics/lesson10_discard_patterns/text.html
new file mode 100644
index 0000000..46dc79b
--- /dev/null
+++ b/src/content/chapter0_basics/lesson10_discard_patterns/text.html
@@ -0,0 +1,10 @@
+<p>
+ If a variable is assigned but not used then Gleam will emit a warning.
+</p>
+<p>
+ If a variable is intended not to be use then the name can be prefixed with an
+ underscore, silencing the warning.
+</p>
+<p>
+ Try changing the variable name to <code>score</code> to see the warning.
+</p>
diff --git a/src/content/chapter0_basics/lesson11_type_annotations/code.gleam b/src/content/chapter0_basics/lesson11_type_annotations/code.gleam
new file mode 100644
index 0000000..1299c2f
--- /dev/null
+++ b/src/content/chapter0_basics/lesson11_type_annotations/code.gleam
@@ -0,0 +1,7 @@
+pub fn main() {
+ let _name: String = "Gleam"
+
+ let _is_cool: Bool = True
+
+ let _version: Int = 1
+}
diff --git a/src/content/chapter0_basics/lesson11_type_annotations/text.html b/src/content/chapter0_basics/lesson11_type_annotations/text.html
new file mode 100644
index 0000000..8738a15
--- /dev/null
+++ b/src/content/chapter0_basics/lesson11_type_annotations/text.html
@@ -0,0 +1,15 @@
+<p>
+ Let assignments can be written with a type annotation after the name.
+</p>
+<p>
+ 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.
+</p>
+<p>
+ Typically Gleam code will not have type annotations for assignments.
+</p>
+<p>
+ Try changing a type annotation to something incorrect to see the compile
+ error.
+</p>
diff --git a/src/content/chapter0_basics/lesson12_blocks/code.gleam b/src/content/chapter0_basics/lesson12_blocks/code.gleam
new file mode 100644
index 0000000..31e4729
--- /dev/null
+++ b/src/content/chapter0_basics/lesson12_blocks/code.gleam
@@ -0,0 +1,13 @@
+import gleam/io
+
+pub fn main() {
+ let fahrenheit = {
+ let degrees = 64
+ degrees
+ }
+ // io.debug(degrees) // <- This will not compile
+
+ // Changing order of evaluation
+ let celsius = { fahrenheit - 32 } * 5 / 9
+ io.debug(celsius)
+}
diff --git a/src/content/chapter0_basics/lesson12_blocks/text.html b/src/content/chapter0_basics/lesson12_blocks/text.html
new file mode 100644
index 0000000..bc82e39
--- /dev/null
+++ b/src/content/chapter0_basics/lesson12_blocks/text.html
@@ -0,0 +1,23 @@
+<p>
+ 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.
+</p>
+<p>
+ Any variables assigned within the block can only be used within the block.
+</p>
+<p>
+ Try uncommenting <code>io.debug(degrees)</code> to see the compile error from
+ trying to use a variable that is not in scope.
+</p>
+<p>
+ Blocks can also be used to change the order of evaluation of binary operators
+ expressions.
+</p>
+<p>
+ <code>*</code> binds more tightly than <code>+</code> so the expression
+ <code>1 + 2 * 3</code> evaluates to 7. If the <code>1 + 2</code> should be
+ evaluated first to make the expression evaluate to 9 then the expression can be
+ wrapped in a block: <code>{ 1 + 2 } * 3</code>. This is similar to grouping
+ with parentheses in some other languages.
+</p>
diff --git a/src/content/chapter0_basics/lesson13_lists/code.gleam b/src/content/chapter0_basics/lesson13_lists/code.gleam
new file mode 100644
index 0000000..646ad6e
--- /dev/null
+++ b/src/content/chapter0_basics/lesson13_lists/code.gleam
@@ -0,0 +1,16 @@
+import gleam/io
+
+pub fn main() {
+ let ints = [1, 2, 3]
+
+ io.debug(ints)
+
+ // Immutably prepend
+ io.debug([-1, 0, ..ints])
+
+ // Uncomment this to see the error
+ // io.debug(["zero", ..ints])
+
+ // The original lists are unchanged
+ io.debug(ints)
+}
diff --git a/src/content/chapter0_basics/lesson13_lists/text.html b/src/content/chapter0_basics/lesson13_lists/text.html
new file mode 100644
index 0000000..dd07cd7
--- /dev/null
+++ b/src/content/chapter0_basics/lesson13_lists/text.html
@@ -0,0 +1,19 @@
+<p>
+ Lists are ordered collections of values.
+</p>
+<p>
+ <code>List</code> is a generic type, having a type parameter
+ for the type of values it contains. A list of ints has the type
+ <code>List(Int)</code>, and a list of strings has the type
+ <code>List(String)</code>.
+</p>
+<p>
+ Lists are immutable single-linked lists, meaning they are very efficient to
+ add and remove elements from the front of the list.
+</p>
+<p>
+ 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 but when they are written a list is not the right
+ choice of data structure.
+</p>
diff --git a/src/content/chapter0_basics/lesson14_list_functions/code.gleam b/src/content/chapter0_basics/lesson14_list_functions/code.gleam
new file mode 100644
index 0000000..92d1cc6
--- /dev/null
+++ b/src/content/chapter0_basics/lesson14_list_functions/code.gleam
@@ -0,0 +1,15 @@
+import gleam/io
+import gleam/list
+
+pub fn main() {
+ let ints = [0, 1, 2, 3, 4, 5]
+
+ let doubled = list.map(ints, fn(x) { x * 2 })
+ io.debug(doubled)
+
+ let even = list.filter(ints, fn(x) { x % 2 == 0 })
+ io.debug(even)
+
+ let total = list.fold(ints, 0, fn(count, e) { count + e })
+ io.debug(total)
+}
diff --git a/src/content/chapter0_basics/lesson14_list_functions/text.html b/src/content/chapter0_basics/lesson14_list_functions/text.html
new file mode 100644
index 0000000..e143654
--- /dev/null
+++ b/src/content/chapter0_basics/lesson14_list_functions/text.html
@@ -0,0 +1,25 @@
+<p>
+ The <a href="https://hexdocs.pm/gleam_stdlib/gleam/list.html"><code>gleam/list</code></a>
+ standard library module contains functions for working with lists. A Gleam
+ program will likely make heavy use of this module.
+</p>
+
+<p>
+ <a href="https://hexdocs.pm/gleam_stdlib/gleam/list.html#map"><code>map</code></a>
+ makes a new list by running a function on each element in a list.
+</p>
+<p>
+ <a href="https://hexdocs.pm/gleam_stdlib/gleam/list.html#filter"><code>filter</code></a>
+ makes a new list containing only the elements for which a function returns
+ true.
+</p>
+<p>
+ <a href="https://hexdocs.pm/gleam_stdlib/gleam/list.html#fold"><code>fold</code></a>
+ 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.
+</p>
+<p>
+ It's worth getting familiar with all the functions in this module when writing
+ Gleam code.
+</p>
diff --git a/src/content/chapter1_functions/lesson00_functions/code.gleam b/src/content/chapter1_functions/lesson00_functions/code.gleam
new file mode 100644
index 0000000..220e58d
--- /dev/null
+++ b/src/content/chapter1_functions/lesson00_functions/code.gleam
@@ -0,0 +1,13 @@
+import gleam/io
+
+pub fn main() {
+ io.debug(double(10))
+}
+
+fn double(a: Int) -> Int {
+ multiply(a, 2)
+}
+
+fn multiply(a: Int, b: Int) -> Int {
+ a * b
+}
diff --git a/src/content/chapter1_functions/lesson00_functions/text.html b/src/content/chapter1_functions/lesson00_functions/text.html
new file mode 100644
index 0000000..32d5bed
--- /dev/null
+++ b/src/content/chapter1_functions/lesson00_functions/text.html
@@ -0,0 +1,14 @@
+<p>
+ The <code>fn</code> keyword is used to define new functions.
+</p>
+<p>
+ The <code>double</code> and <code>multiply</code> functions are defined
+ without the <code>pub</code> keyword. This makes them <em>private</em>
+ functions, they can only be used within this module. If another module
+ attempted to use them it would result in a compiler error.
+</p>
+<p>
+ 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.
+</p>
diff --git a/src/content/chapter1_functions/lesson01_higher_order_functions/code.gleam b/src/content/chapter1_functions/lesson01_higher_order_functions/code.gleam
new file mode 100644
index 0000000..e3fb3e7
--- /dev/null
+++ b/src/content/chapter1_functions/lesson01_higher_order_functions/code.gleam
@@ -0,0 +1,18 @@
+import gleam/io
+
+pub fn main() {
+ // Call a function with another function
+ io.debug(twice(1, add_one))
+
+ // Functions can be assigned to variables
+ let function = add_one
+ io.debug(function(100))
+}
+
+fn twice(argument: Int, function: fn(Int) -> Int) -> Int {
+ function(function(argument))
+}
+
+fn add_one(argument: Int) -> Int {
+ argument + 1
+}
diff --git a/src/content/chapter1_functions/lesson01_higher_order_functions/text.html b/src/content/chapter1_functions/lesson01_higher_order_functions/text.html
new file mode 100644
index 0000000..3343e4d
--- /dev/null
+++ b/src/content/chapter1_functions/lesson01_higher_order_functions/text.html
@@ -0,0 +1,12 @@
+<p>
+ In Gleam functions are values. They can be assigned to variables, passed to
+ other functions, and anything else you can do with values.
+</p>
+<p>
+ Here the function <code>add_one</code> is being passed as an argument to the
+ <code>twice</code> function.
+</p>
+<p>
+ Notice the <code>fn</code> keyword is also used to describe the type of the
+ function that <code>twice</code> takes as its second argument.
+</p>
diff --git a/src/content/chapter1_functions/lesson02_anonymous_functions/code.gleam b/src/content/chapter1_functions/lesson02_anonymous_functions/code.gleam
new file mode 100644
index 0000000..2b037e0
--- /dev/null
+++ b/src/content/chapter1_functions/lesson02_anonymous_functions/code.gleam
@@ -0,0 +1,14 @@
+import gleam/io
+
+pub fn main() {
+ // Assign an anonymous function to a variable
+ let add_one = fn(a) { a + 1 }
+ io.debug(twice(1, add_one))
+
+ // Pass an anonymous function as an argument
+ io.debug(twice(1, fn(a) { a * 2 }))
+}
+
+fn twice(argument: Int, function: fn(Int) -> Int) -> Int {
+ function(function(argument))
+}
diff --git a/src/content/chapter1_functions/lesson02_anonymous_functions/text.html b/src/content/chapter1_functions/lesson02_anonymous_functions/text.html
new file mode 100644
index 0000000..f7bea3f
--- /dev/null
+++ b/src/content/chapter1_functions/lesson02_anonymous_functions/text.html
@@ -0,0 +1,7 @@
+<p>
+ As well as module-level named functions, Gleam has anonymous function
+ literals.
+</p>
+<p>
+ Anonymous functions can be used interchangeably with named functions.
+</p>
diff --git a/src/content/chapter1_functions/lesson03_function_captures/code.gleam b/src/content/chapter1_functions/lesson03_function_captures/code.gleam
new file mode 100644
index 0000000..35f3412
--- /dev/null
+++ b/src/content/chapter1_functions/lesson03_function_captures/code.gleam
@@ -0,0 +1,14 @@
+import gleam/io
+
+pub fn main() {
+ // These two statements are equivalent
+ let add_one_v1 = fn(x) { add(1, x) }
+ let add_one_v2 = add(1, _)
+
+ io.debug(add_one_v1(10))
+ io.debug(add_one_v2(10))
+}
+
+fn add(a: Int, b: Int) -> Int {
+ a + b
+}
diff --git a/src/content/chapter1_functions/lesson03_function_captures/text.html b/src/content/chapter1_functions/lesson03_function_captures/text.html
new file mode 100644
index 0000000..afa87a3
--- /dev/null
+++ b/src/content/chapter1_functions/lesson03_function_captures/text.html
@@ -0,0 +1,11 @@
+<p>
+ 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.
+</p>
+<p>
+ The anonymous function <code>fn(a) { some_function(..., a, ...) }</code> can
+ be written as <code>some_function(..., _, ...)</code>, with any number of
+ other arguments passed to the inner function. The underscore <code>_</code> is
+ a placeholder for the final argument.
+</p>
diff --git a/src/content/chapter1_functions/lesson04_generic_functions/code.gleam b/src/content/chapter1_functions/lesson04_generic_functions/code.gleam
new file mode 100644
index 0000000..e232bf8
--- /dev/null
+++ b/src/content/chapter1_functions/lesson04_generic_functions/code.gleam
@@ -0,0 +1,19 @@
+import gleam/io
+
+pub fn main() {
+ let add_one = fn(x) { x + 1 }
+ let exclaim = fn(x) { x <> "!" }
+
+ // Invalid, Int and String are not the same type
+ // twice(10, exclaim)
+
+ // Here the type variable is replaced by the type Int
+ io.debug(twice(10, add_one))
+
+ // Here the type variable is replaced by the type String
+ io.debug(twice("Hello", exclaim))
+}
+
+fn twice(argument: value, function: fn(value) -> value) -> value {
+ function(function(argument))
+}
diff --git a/src/content/chapter1_functions/lesson04_generic_functions/text.html b/src/content/chapter1_functions/lesson04_generic_functions/text.html
new file mode 100644
index 0000000..1369c93
--- /dev/null
+++ b/src/content/chapter1_functions/lesson04_generic_functions/text.html
@@ -0,0 +1,25 @@
+<p>
+ Up until now each function has accepted precisely one type for each of its
+ arguments.
+</p>
+<p>
+ The <code>twice</code> function for example 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.
+</p>
+<p>
+ To enable this Gleam support <em>generics</em>, also known as <em>parametric
+ polymorphism</em>.
+</p>
+<p>
+ This works by instead of specifying a concrete type, a type variable is used
+ which stands in for whatever specific type is being used when the function is
+ called. These type variable are written with a lowercase name.
+</p>
+<p>
+ Type variables are not like an <code>any</code> type, they get replaced with a
+ specific type each time the function is called. Try uncommenting
+ <code>twice(10, exclaim)</code> to see the compiler error from trying to use a
+ type variable as an int and a string at the same time.
+</p>
diff --git a/src/content/chapter1_functions/lesson05_pipelines/code.gleam b/src/content/chapter1_functions/lesson05_pipelines/code.gleam
new file mode 100644
index 0000000..ec9b805
--- /dev/null
+++ b/src/content/chapter1_functions/lesson05_pipelines/code.gleam
@@ -0,0 +1,19 @@
+import gleam/io
+import gleam/string
+
+pub fn main() {
+ // Without the pipe operator
+ io.debug(string.drop_left(string.drop_right("Hello, Joe!", 1), 7))
+
+ // With the pipe operator
+ "Hello, Mike!"
+ |> string.drop_right(1)
+ |> string.drop_left(7)
+ |> io.debug
+
+ // Changing order with function capturing
+ "1"
+ |> string.append("2")
+ |> string.append("3", _)
+ |> io.debug
+}
diff --git a/src/content/chapter1_functions/lesson05_pipelines/text.html b/src/content/chapter1_functions/lesson05_pipelines/text.html
new file mode 100644
index 0000000..783ade9
--- /dev/null
+++ b/src/content/chapter1_functions/lesson05_pipelines/text.html
@@ -0,0 +1,25 @@
+<p>
+ 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.
+</p>
+<p>
+ Gleam's pipe operator <code>|></code> helps with this problem by allowing you
+ to write code top-to-bottom.
+</p>
+<p>
+ The pipe operator takes the result of the expression on its left and passes it
+ as an argument to the function on its right.
+</p>
+<p>
+ It will first check to see if the left-hand value could be used as the first
+ argument to the call. For example, <code>a |> b(1, 2)</code> would become
+ <code>b(a, 1, 2)</code>. If not, it falls back to calling the result of the
+ right-hand side as a function, e.g., <code>b(1, 2)(a)</code>
+</p>
+<p>
+ 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.
+</p>
diff --git a/src/content/chapter1_functions/lesson06_labelled_arguments/code.gleam b/src/content/chapter1_functions/lesson06_labelled_arguments/code.gleam
new file mode 100644
index 0000000..25bb8c1
--- /dev/null
+++ b/src/content/chapter1_functions/lesson06_labelled_arguments/code.gleam
@@ -0,0 +1,16 @@
+import gleam/io
+
+pub fn main() {
+ // Without using labels
+ io.debug(calculate(1, 2, 3))
+
+ // Using the labels
+ io.debug(calculate(1, add: 2, multiply: 3))
+
+ // Using the labels in a different order
+ io.debug(calculate(1, multiply: 3, add: 2))
+}
+
+fn calculate(value: Int, add addend: Int, multiply multiplier: Int) {
+ value * multiplier + addend
+}
diff --git a/src/content/chapter1_functions/lesson06_labelled_arguments/text.html b/src/content/chapter1_functions/lesson06_labelled_arguments/text.html
new file mode 100644
index 0000000..b1d771c
--- /dev/null
+++ b/src/content/chapter1_functions/lesson06_labelled_arguments/text.html
@@ -0,0 +1,23 @@
+<p>
+ When functions take several arguments it can be difficult to remember what the
+ arguments are, and what order they are expected in.
+</p>
+<p>
+ 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.
+</p>
+<p>
+ When labelled arguments are used the order of the arguments does not matter,
+ but all unlabelled arguments must come before labelled arguments.
+</p>
+<p>
+ There is no performance cost to using labelled arguments, it does not allocate
+ a dictionary or perform any other runtime work.
+</p>
+<p>
+ Labels are optional when calling a function, it is up to the programmer to
+ decide what is clearest in their code.
+</p>
+
+
diff --git a/src/content/chapter1_functions/lesson099_documentation_comments/code.gleam b/src/content/chapter1_functions/lesson099_documentation_comments/code.gleam
new file mode 100644
index 0000000..a84dce6
--- /dev/null
+++ b/src/content/chapter1_functions/lesson099_documentation_comments/code.gleam
@@ -0,0 +1,19 @@
+//// A module containing some unusual functions and types.
+
+/// A type where the value can never be constructed.
+/// Can you work out why?
+pub type Never {
+ Never(Never)
+}
+
+/// Call a function twice with an initial value.
+///
+pub fn twice(argument: value, function: fn(value) -> value) -> value {
+ function(function(argument))
+}
+
+/// Call a function three times with an initial value.
+///
+pub fn thrice(argument: value, function: fn(value) -> value) -> value {
+ function(function(function(argument)))
+}
diff --git a/src/content/chapter1_functions/lesson099_documentation_comments/text.html b/src/content/chapter1_functions/lesson099_documentation_comments/text.html
new file mode 100644
index 0000000..c27bac6
--- /dev/null
+++ b/src/content/chapter1_functions/lesson099_documentation_comments/text.html
@@ -0,0 +1,16 @@
+<p>
+ Documentation and comments are important tools for making your code easier to
+ work with and understand.
+</p>
+<p>
+ As well as regular <code>//</code> comments Gleam has <code>///</code> and
+ <code>////</code> comments which are used for attaching documentation to code.
+</p>
+<p>
+ <code>///</code> is used for documenting types and functions, and should be
+ placed immediately before the type or function it is documenting.
+</p>
+<p>
+ <code>////</code> is used for documenting modules, and should be placed
+ at the top of the module.
+</p>
diff --git a/src/content/chapter2_flow_control/lesson01_case_expressions/code.gleam b/src/content/chapter2_flow_control/lesson01_case_expressions/code.gleam
new file mode 100644
index 0000000..6e1ed46
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson01_case_expressions/code.gleam
@@ -0,0 +1,10 @@
+import gleam/io
+import gleam/int
+
+pub fn main() {
+ let result = case int.random(5) {
+ 0 -> "It's zero!"
+ other -> "It's " <> int.to_string(other)
+ }
+ io.debug(result)
+}
diff --git a/src/content/chapter2_flow_control/lesson01_case_expressions/text.html b/src/content/chapter2_flow_control/lesson01_case_expressions/text.html
new file mode 100644
index 0000000..7e9ac11
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson01_case_expressions/text.html
@@ -0,0 +1,7 @@
+<p>
+ Patterns in case expressions can also assign variables.
+</p>
+<p>
+ 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.
+</p>
diff --git a/src/content/chapter2_flow_control/lesson02_variable_patterns/code.gleam b/src/content/chapter2_flow_control/lesson02_variable_patterns/code.gleam
new file mode 100644
index 0000000..7bcc93c
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson02_variable_patterns/code.gleam
@@ -0,0 +1,16 @@
+import gleam/io
+import gleam/int
+
+pub fn main() {
+ let x = int.random(5)
+ io.debug(x)
+
+ let result = case x {
+ // Match specific values
+ 0 -> "Zero"
+ 1 -> "One"
+ // Match any other value
+ _ -> "Other"
+ }
+ io.debug(result)
+}
diff --git a/src/content/chapter2_flow_control/lesson02_variable_patterns/text.html b/src/content/chapter2_flow_control/lesson02_variable_patterns/text.html
new file mode 100644
index 0000000..8154979
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson02_variable_patterns/text.html
@@ -0,0 +1,17 @@
+<p>
+ 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.
+</p>
+<p>
+ It allows the programmer to say "if the data has this shape then run this
+ code", a process called called <em>pattern matching</em>.
+</p>
+<p>
+ Gleam performs <em>exhaustiveness checking</em> 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.
+</p>
+<p>
+ Try commenting out patterns or adding new redundant ones, and see what
+ problems the compiler reports.
+</p>
diff --git a/src/content/chapter2_flow_control/lesson03_string_patterns/code.gleam b/src/content/chapter2_flow_control/lesson03_string_patterns/code.gleam
new file mode 100644
index 0000000..d1441a0
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson03_string_patterns/code.gleam
@@ -0,0 +1,14 @@
+import gleam/io
+
+pub fn main() {
+ io.debug(get_name("Hello, Joe"))
+ io.debug(get_name("Hello, Mike"))
+ io.debug(get_name("System still working?"))
+}
+
+fn get_name(x: String) -> String {
+ case x {
+ "Hello, " <> name -> name
+ _ -> "Unknown"
+ }
+}
diff --git a/src/content/chapter2_flow_control/lesson03_string_patterns/text.html b/src/content/chapter2_flow_control/lesson03_string_patterns/text.html
new file mode 100644
index 0000000..0dd3274
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson03_string_patterns/text.html
@@ -0,0 +1,9 @@
+<p>
+ When pattern matching on strings the <code>&lt;&gt;</code> operator can be
+ used to match on strings with a specific prefix.
+</p>
+<p>
+ The pattern <code>"hello " <> name</code> matches any string that starts with
+ <code>"hello "</code> and asigns the rest of the string to the variable
+ <code>name</code>.
+</p>
diff --git a/src/content/chapter2_flow_control/lesson04_list_patterns/code.gleam b/src/content/chapter2_flow_control/lesson04_list_patterns/code.gleam
new file mode 100644
index 0000000..e767d20
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson04_list_patterns/code.gleam
@@ -0,0 +1,17 @@
+import gleam/io
+import gleam/int
+import gleam/list
+
+pub fn main() {
+ let x = list.repeat(int.random(5), times: int.random(3))
+ io.debug(x)
+
+ let result = case x {
+ [] -> "Empty list"
+ [1] -> "List of just 1"
+ [4, ..] -> "List starting with 4"
+ [_, _] -> "List of 2 elements"
+ _ -> "Some other list"
+ }
+ io.debug(result)
+}
diff --git a/src/content/chapter2_flow_control/lesson04_list_patterns/text.html b/src/content/chapter2_flow_control/lesson04_list_patterns/text.html
new file mode 100644
index 0000000..de55eef
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson04_list_patterns/text.html
@@ -0,0 +1,15 @@
+<p>
+ Lists and the values they contain can be pattern matched on in case
+ expressions.
+</p>
+<p>
+ List patterns match on specific lengths of lists. The pattern <code>[]</code>
+ matches an empty list, and the pattern <code>[_]</code> matches a list with
+ one element. They will not match on lists with other lengths.
+</p>
+<p>
+ The spread pattern <code>..</code> can be used to match the rest of the list.
+ The pattern <code>[1, ..]</code> matches any list that starts with
+ <code>1</code>. The pattern <code>[_, _, ..]</code> matches any list that has
+ at least two elements.
+</p>
diff --git a/src/content/chapter2_flow_control/lesson05_list_recursion/code.gleam b/src/content/chapter2_flow_control/lesson05_list_recursion/code.gleam
new file mode 100644
index 0000000..370675a
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson05_list_recursion/code.gleam
@@ -0,0 +1,13 @@
+import gleam/io
+
+pub fn main() {
+ let sum = sum_list([18, 56, 35, 85, 91], 0)
+ io.debug(sum)
+}
+
+fn sum_list(list: List(Int), total: Int) -> Int {
+ case list {
+ [first, ..rest] -> sum_list(rest, total + first)
+ [] -> total
+ }
+}
diff --git a/src/content/chapter2_flow_control/lesson05_list_recursion/text.html b/src/content/chapter2_flow_control/lesson05_list_recursion/text.html
new file mode 100644
index 0000000..7f2351d
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson05_list_recursion/text.html
@@ -0,0 +1,22 @@
+<p>
+ Most commonly functions in the
+ <a href="https://hexdocs.pm/gleam_stdlib/gleam/list.html"><code>gleam/list</code></a>
+ module are used to iterate across a list, but at times you may prefer
+ to work with the list directly.
+</p>
+<p>
+ Gleam doesn't have a looping syntax, instead iteration is done through
+ recursion and pattern matching.
+</p>
+<p>
+ The <code>[first, ..rest]</code> pattern matches on a list with at least one
+ element, assigning the first element to the variable <code>first</code> and
+ the rest of the list to the variable <code>rest</code>.
+ By using this pattern and a pattern for the empty list <code>[]</code> a
+ function can run code on each element of a list until the end is reached.
+</p>
+<p>
+ This code sums a list by recursing over the list and adding each int to a
+ <code>total</code> argument, returning it when the end is reached.
+</p>
+
diff --git a/src/content/chapter2_flow_control/lesson06_multiple_subjects/code.gleam b/src/content/chapter2_flow_control/lesson06_multiple_subjects/code.gleam
new file mode 100644
index 0000000..d7aa34a
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson06_multiple_subjects/code.gleam
@@ -0,0 +1,17 @@
+import gleam/io
+import gleam/int
+
+pub fn main() {
+ let x = int.random(2)
+ let y = int.random(2)
+ io.debug(x)
+ io.debug(y)
+
+ let result = case x, y {
+ 0, 0 -> "Both are zero"
+ 0, _ -> "First is zero"
+ _, 0 -> "Second is zero"
+ _, _ -> "Neither are zero"
+ }
+ io.debug(result)
+}
diff --git a/src/content/chapter2_flow_control/lesson06_multiple_subjects/text.html b/src/content/chapter2_flow_control/lesson06_multiple_subjects/text.html
new file mode 100644
index 0000000..26a7ea3
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson06_multiple_subjects/text.html
@@ -0,0 +1,13 @@
+<p>
+ Sometimes it is useful to pattern match on multiple values at the same time in
+ one case experession.
+</p>
+<p>
+ To do this you can give multiple subjects and multiple patterns, separated
+ commas.
+</p>
+<p>
+ When matching on multiple subjects there must be the same number of patterns
+ as there are subjects. Try removing one of the <code>_,</code> sub-patterns to
+ see the compile time error that is returned.
+</p>
diff --git a/src/content/chapter2_flow_control/lesson07_alternative_patterns/code.gleam b/src/content/chapter2_flow_control/lesson07_alternative_patterns/code.gleam
new file mode 100644
index 0000000..06a6562
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson07_alternative_patterns/code.gleam
@@ -0,0 +1,14 @@
+import gleam/io
+import gleam/int
+
+pub fn main() {
+ let number = int.random(10)
+ io.debug(number)
+
+ let result = case number {
+ 2 | 4 | 6 | 8 -> "This is an even number"
+ 1 | 3 | 5 | 7 -> "This is an odd number"
+ _ -> "I'm not sure"
+ }
+ io.debug(result)
+}
diff --git a/src/content/chapter2_flow_control/lesson07_alternative_patterns/text.html b/src/content/chapter2_flow_control/lesson07_alternative_patterns/text.html
new file mode 100644
index 0000000..10ad731
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson07_alternative_patterns/text.html
@@ -0,0 +1,17 @@
+<p>
+ Alternative patterns can be given for a case clause using the
+ <code>|</code> operator. If any of the patterns match then the clause matches.
+</p>
+<p>
+ When matching on multiple subjects there must be the same number of patterns
+ as there are subjects. Try removing one of the <code>_,</code> sub-patterns to
+ see the compile time error that is returned.
+</p>
+<p>
+ 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.
+</p>
+<p>
+ Currently it is not possible to have nested alternative patterns, so the
+ pattern <code>[1 | 2 | 3]</code> is not valid.
+</p>
diff --git a/src/content/chapter2_flow_control/lesson08_pattern_aliases/code.gleam b/src/content/chapter2_flow_control/lesson08_pattern_aliases/code.gleam
new file mode 100644
index 0000000..ee40a26
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson08_pattern_aliases/code.gleam
@@ -0,0 +1,15 @@
+import gleam/io
+
+pub fn main() {
+ io.debug(get_first_non_empty([[], [1, 2, 3], [4, 5]]))
+ io.debug(get_first_non_empty([[1, 2], [3, 4, 5], []]))
+ io.debug(get_first_non_empty([[], [], []]))
+}
+
+fn get_first_non_empty(lists: List(List(t))) -> List(t) {
+ case lists {
+ [[_, ..] as first, ..] -> first
+ [_, ..rest] -> get_first_non_empty(rest)
+ [] -> []
+ }
+}
diff --git a/src/content/chapter2_flow_control/lesson08_pattern_aliases/text.html b/src/content/chapter2_flow_control/lesson08_pattern_aliases/text.html
new file mode 100644
index 0000000..b737eb8
--- /dev/null
+++ b/src/content/chapter2_flow_control/lesson08_pattern_aliases/text.html
@@ -0,0 +1,7 @@
+<p>
+ The <code>as</code> operator can be used to assign sub patterns to variables.
+</p>
+<p>
+ The pattern <code>[_, ..] as it</code> will match any non-empty list and
+ assign that list to the variable <code>it</code>.
+</p>
diff --git a/src/content/chapter3_data_types/lesson00_tuples/code.gleam b/src/content/chapter3_data_types/lesson00_tuples/code.gleam
new file mode 100644
index 0000000..d5c6313
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson00_tuples/code.gleam
@@ -0,0 +1,10 @@
+import gleam/io
+
+pub fn main() {
+ let triple = #(1, 2.2, "three")
+ io.debug(triple)
+
+ let #(a, _, _) = triple
+ io.debug(a)
+ io.debug(triple.1)
+}
diff --git a/src/content/chapter3_data_types/lesson00_tuples/text.html b/src/content/chapter3_data_types/lesson00_tuples/text.html
new file mode 100644
index 0000000..f121a9d
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson00_tuples/text.html
@@ -0,0 +1,20 @@
+<p>
+ 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.
+</p>
+<p>
+ The tuple access syntax can be used to get elements from a tuple without
+ pattern matching. <code>some_tuple.0</code> gets the first element,
+ <code>some_tuple.1</code> gets the second element, etc.
+</p>
+<p>
+ Tuples are generic types, they have type parameters for the types they
+ contain. <code>#(1, "Hi!")</code> has the type <code>#(Int, String)</code>,
+ and <code>#(1.4, 10, 48)</code> has the type <code>#(Float, Int, Int)</code>.
+</p>
+<p>
+ Tuples are most commonly used to return 2 or 3 values from a function. Other
+ times it is often is clearer to use a <em>custom type</em>, which we will
+ cover next.
+</p>
diff --git a/src/content/chapter3_data_types/lesson01_custom_types/code.gleam b/src/content/chapter3_data_types/lesson01_custom_types/code.gleam
new file mode 100644
index 0000000..35629bf
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson01_custom_types/code.gleam
@@ -0,0 +1,22 @@
+import gleam/io
+
+pub type Season {
+ Spring
+ Summer
+ Autumn
+ Winter
+}
+
+pub fn main() {
+ io.debug(weather(Spring))
+ io.debug(weather(Autumn))
+}
+
+fn weather(season: Season) -> String {
+ case season {
+ Spring -> "Mild"
+ Summer -> "Hot"
+ Autumn -> "Windy"
+ Winter -> "Cold"
+ }
+}
diff --git a/src/content/chapter3_data_types/lesson01_custom_types/text.html b/src/content/chapter3_data_types/lesson01_custom_types/text.html
new file mode 100644
index 0000000..dad6d12
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson01_custom_types/text.html
@@ -0,0 +1,9 @@
+<p>
+ Gleam has a few built in types such as <code>Int</code>, <code>String</code>,
+ but custom types allow the creation of entirely new types.
+</p>
+<p>
+ A custom type is defined with the <code>type</code> keyword followed by a
+ constructor for each <em>variant</em> of the type.
+</p>
+<p>Custom type variants can be pattern matched on using a case expression.</p>
diff --git a/src/content/chapter3_data_types/lesson02_records/code.gleam b/src/content/chapter3_data_types/lesson02_records/code.gleam
new file mode 100644
index 0000000..bd6da3c
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson02_records/code.gleam
@@ -0,0 +1,17 @@
+import gleam/io
+
+pub type SchoolPerson {
+ Teacher(name: String, subject: String)
+ Student(String)
+}
+
+pub fn main() {
+ let teacher1 = Teacher("Mr Schofield", "Physics")
+ let teacher2 = Teacher(name: "Miss Percy", subject: "Physics")
+ let student1 = Student("Koushiar")
+ let student2 = Student("Naomi")
+ let student3 = Student("Shaheer")
+
+ let school = [teacher1, teacher2, student1, student2, student3]
+ io.debug(school)
+}
diff --git a/src/content/chapter3_data_types/lesson02_records/text.html b/src/content/chapter3_data_types/lesson02_records/text.html
new file mode 100644
index 0000000..f515ccd
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson02_records/text.html
@@ -0,0 +1,10 @@
+<p>Variants of a record can hold other data within them.</p>
+<p>
+ These fields 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.
+</p>
+<p>
+ 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.
+</p>
diff --git a/src/content/chapter3_data_types/lesson03_record_accessors/code.gleam b/src/content/chapter3_data_types/lesson03_record_accessors/code.gleam
new file mode 100644
index 0000000..63ca721
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson03_record_accessors/code.gleam
@@ -0,0 +1,15 @@
+import gleam/io
+
+pub type SchoolPerson {
+ Teacher(name: String, subject: String)
+ Student(name: String)
+}
+
+pub fn main() {
+ let teacher = Teacher("Mr Schofield", "Physics")
+ let student = Student("Koushiar")
+
+ io.debug(teacher.name)
+ io.debug(student.name)
+ // io.debug(teacher.subject)
+}
diff --git a/src/content/chapter3_data_types/lesson03_record_accessors/text.html b/src/content/chapter3_data_types/lesson03_record_accessors/text.html
new file mode 100644
index 0000000..e8bbbc2
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson03_record_accessors/text.html
@@ -0,0 +1,18 @@
+<p>
+ The record accessor syntax <code>record.field_label</code> can be used to get
+ contained values from a custom type record.
+</p>
+<p>
+ The accessor syntax can only be used for fields that are in the same position
+ and have the same type for all variants of the custom type.
+</p>
+<p>
+ The <code>name</code> field is in the first position and has type
+ <code>String</code> for all variants, so it can be accessed.
+</p>
+<p>
+ The <code>subject</code> field is absent on the <code>Student</code> variant,
+ so it cannot be used on any variant of type <code>SchoolPerson</code>.
+ Uncomment the <code>teacher.subject</code> line to see the compile error from
+ trying to use this accessor.
+</p>
diff --git a/src/content/chapter3_data_types/lesson04_record_updates/code.gleam b/src/content/chapter3_data_types/lesson04_record_updates/code.gleam
new file mode 100644
index 0000000..ed7b45b
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson04_record_updates/code.gleam
@@ -0,0 +1,15 @@
+import gleam/io
+
+pub type SchoolPerson {
+ Teacher(name: String, subject: String, floor: Int, room: Int)
+}
+
+pub fn main() {
+ let teacher1 = Teacher(name: "Mr Dodd", subject: "ICT", floor: 2, room: 2)
+
+ // Use the update syntax
+ let teacher2 = Teacher(..teacher1, subject: "PE", room: 6)
+
+ io.debug(teacher1)
+ io.debug(teacher2)
+}
diff --git a/src/content/chapter3_data_types/lesson04_record_updates/text.html b/src/content/chapter3_data_types/lesson04_record_updates/text.html
new file mode 100644
index 0000000..f23d7cd
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson04_record_updates/text.html
@@ -0,0 +1,12 @@
+<p>
+ 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.
+</p>
+<p>
+ The accessor syntax can only be used for fields that are in the same position
+ and have the same type for all variants of the custom type.
+</p>
+<p>
+ Gleam is an immutable language, so using the record update syntax does not
+ mutate or otherwise change the original record.
+</p>
diff --git a/src/content/chapter3_data_types/lesson05_nil/code.gleam b/src/content/chapter3_data_types/lesson05_nil/code.gleam
new file mode 100644
index 0000000..c28080b
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson05_nil/code.gleam
@@ -0,0 +1,11 @@
+import gleam/io
+
+pub fn main() {
+ let x = Nil
+ io.debug(x)
+
+ // let y: List(String) = Nil
+
+ let result = io.println("Hello!")
+ io.debug(result == Nil)
+}
diff --git a/src/content/chapter3_data_types/lesson05_nil/text.html b/src/content/chapter3_data_types/lesson05_nil/text.html
new file mode 100644
index 0000000..3416643
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson05_nil/text.html
@@ -0,0 +1,15 @@
+<p>
+ <code>Nil</code> is Gleam's unit type. It is a value that is returned by
+ functions that have nothing else to return, as all functions much return
+ something.
+</p>
+<p>
+ <code>Nil</code> is not a valid value of any other types, that is values in
+ Gleam are not nullable. If the type of a value is <code>Nil</code> then it is
+ the value <code>nil</code>. If it is some other type then the value is not
+ <code>Nil</code>.
+</p>
+<p>
+ Uncomment the line that assigns <code>Nil</code> to a variable with an
+ incompatible type annotation to see the comile time error it produces.
+</p>
diff --git a/src/content/chapter3_data_types/lesson06_bit_arrays/code.gleam b/src/content/chapter3_data_types/lesson06_bit_arrays/code.gleam
new file mode 100644
index 0000000..dc772ca
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson06_bit_arrays/code.gleam
@@ -0,0 +1,13 @@
+import gleam/io
+
+pub fn main() {
+ // 8 bit int. In binary: 00000011
+ io.debug(<<3>>)
+ io.debug(<<3>> == <<3:size(8)>>)
+
+ // 16 bit int. In binary: 0001100000000011
+ io.debug(<<6147:size(16)>>)
+
+ // A bit array of UTF8 data
+ io.debug(<<"Hello, Joe!":utf8>>)
+}
diff --git a/src/content/chapter3_data_types/lesson06_bit_arrays/text.html b/src/content/chapter3_data_types/lesson06_bit_arrays/text.html
new file mode 100644
index 0000000..3214db1
--- /dev/null
+++ b/src/content/chapter3_data_types/lesson06_bit_arrays/text.html
@@ -0,0 +1,26 @@
+<p>
+ Bit arrays represent a sequence of 1s and 0s, and are a convenient syntax for
+ constructing and manipulating binary data.
+</p>
+<p>
+ Each segment of a bit array can be given options to specify the representation
+ used for that segment.
+</p>
+<ul>
+ <li><code>size</code>: the size of the segment in bits.</li>
+ <li><code>unit</code>: how many times to repeat the segment.</li>
+ <li><code>bits</code>: a nested bit array of any size.</li>
+ <li><code>bytes</code>: a nested byte-aligned bit array.</li>
+ <li><code>float</code>: a 64 bits floating point number.</li>
+ <li><code>int</code>: an int with a default size of 8 bits.</li>
+ <li><code>big</code>: big endian.</li>
+ <li><code>little</code>: little endian.</li>
+ <li><code>native</code>: the endianness of the processor.</li>
+ <li><code>utf8</code>: utf8 encoded text.</li>
+ <li><code>utf16</code>: utf16 encoded text.</li>
+ <li><code>utf32</code>: utf32 encoded text.</li>
+</ul>
+<p>
+ Bit arrays have limited support when compiling to JavaScript, not all options
+ can be used. Full bit array support will be implemented in future.
+</p>
diff --git a/src/try_gleam.gleam b/src/try_gleam.gleam
index 8c030e9..c788a71 100644
--- a/src/try_gleam.gleam
+++ b/src/try_gleam.gleam
@@ -3,7 +3,6 @@ import gleam/list
import htmb.{h, text}
import gleam/string_builder
import gleam/option.{type Option, None, Some}
-import gleam/pair
import gleam/string
import gleam/result
import simplifile
@@ -25,7 +24,7 @@ const stdlib_external = "build/packages/gleam_stdlib/src"
const compiler_wasm = "../gleam/compiler-wasm/pkg"
-const lessons_src = "lessons/src"
+const content_path = "src/content"
const hello_joe = "import gleam/io
@@ -45,8 +44,8 @@ pub fn main() {
use _ <- result.try(make_prelude_available())
use _ <- result.try(make_stdlib_available())
use _ <- result.try(copy_wasm_compiler())
- use p <- result.try(load_pages())
- use _ <- result.try(write_pages(p))
+ use p <- result.try(load_content())
+ use _ <- result.try(write_content(p))
Ok(Nil)
}
@@ -60,8 +59,12 @@ pub fn main() {
}
}
-type Page {
- Page(
+type Chapter {
+ Chapter(name: String, path: String, lessons: List(Lesson))
+}
+
+type Lesson {
+ Lesson(
name: String,
text: String,
code: String,
@@ -71,118 +74,175 @@ type Page {
)
}
-fn load_pages() -> snag.Result(List(Page)) {
- use lessons <- result.try(
- simplifile.read_directory(lessons_src)
- |> file_error("Failed to read lessons directory"),
- )
+type FileNames {
+ FileNames(path: String, name: String, slug: String)
+}
- let lessons =
- lessons
- |> list.sort(by: string.compare)
- |> list.index_map(pair.new)
-
- use pages <- result.try(
- list.try_map(lessons, fn(pair) {
- let #(lesson, index) = pair
- let path = lessons_src <> "/" <> lesson
- let name =
- lesson
- |> string.split("_")
- |> list.drop(1)
- |> string.join("-")
+fn load_directory_names(path: String) -> snag.Result(List(FileNames)) {
+ use files <- result.map(
+ simplifile.read_directory(path)
+ |> file_error("Failed to read directory " <> path),
+ )
+ files
+ |> list.sort(by: string.compare)
+ |> list.filter(fn(file) { !string.starts_with(file, ".") })
+ |> list.map(fn(file) {
+ let path = path <> "/" <> file
+ let slug =
+ file
+ |> string.split("_")
+ |> list.drop(1)
+ |> string.join("-")
+ let name =
+ slug
+ |> string.replace("-", " ")
+ |> string.capitalise
+ FileNames(path: path, name: name, slug: slug)
+ })
+}
- use code <- result.try(
- simplifile.read(path <> "/code.gleam")
- |> file_error("Failed to read code.gleam"),
- )
+fn load_chapter(names: FileNames) -> snag.Result(Chapter) {
+ let path = "/" <> names.slug
+ use lessons <- result.try(load_directory_names(names.path))
+ use lessons <- result.try(list.try_map(lessons, load_lesson(path, _)))
+ Ok(Chapter(name: names.name, path: path, lessons: lessons))
+}
- use text <- result.try(
- simplifile.read(path <> "/text.html")
- |> file_error("Failed to read text.html"),
- )
+fn read_file(path: String) -> snag.Result(String) {
+ simplifile.read(path)
+ |> file_error("Failed to read file " <> path)
+}
- let path = case index {
- 0 -> "/"
- _ -> "/" <> name
- }
-
- Ok(Page(
- name: name,
- text: text,
- code: code,
- path: path,
- previous: None,
- next: None,
- ))
- }),
- )
+fn load_lesson(chapter_path: String, names: FileNames) -> snag.Result(Lesson) {
+ use code <- result.try(read_file(names.path <> "/code.gleam"))
+ use text <- result.try(read_file(names.path <> "/text.html"))
+
+ Ok(Lesson(
+ name: names.name,
+ text: text,
+ code: code,
+ path: chapter_path
+ <> "/"
+ <> names.slug,
+ previous: None,
+ next: None,
+ ))
+}
- Ok(add_previous_next(pages, [], None))
+fn load_content() -> snag.Result(List(Chapter)) {
+ use chapters <- result.try(load_directory_names(content_path))
+ use chapters <- result.try(list.try_map(chapters, load_chapter))
+ Ok(add_prev_next(chapters, [], Some("/")))
}
-fn write_pages(pages: List(Page)) -> snag.Result(Nil) {
- use _ <- result.try(list.try_each(pages, write_page))
+fn write_content(chapters: List(Chapter)) -> snag.Result(Nil) {
+ let lessons = list.flat_map(chapters, fn(c) { c.lessons })
+ use _ <- result.try(list.try_map(lessons, write_lesson))
- let render = fn(h) { string_builder.to_string(htmb.render(h)) }
- let html =
- string.concat([
- render(h("h2", [], [text("Table of contents")])),
- render(h(
- "ul",
- [],
- list.map(pages, fn(page) {
- h("li", [], [
- h("a", [#("href", page.path)], [
- page.name
- |> string.replace("-", " ")
- |> string.capitalise
- |> text,
- ]),
- ])
- }),
- )),
- ])
-
- let page =
- Page(
+ let lesson =
+ Lesson(
name: "Index",
- text: html,
+ text: index_list_html(chapters),
code: hello_joe,
path: "/index",
previous: None,
next: None,
)
- write_page(page)
+ write_lesson(lesson)
}
-fn write_page(page: Page) -> snag.Result(Nil) {
- let path = public <> page.path
+fn index_chapter_html(chapter: Chapter) -> String {
+ string.concat([
+ render_html(h("h3", [#("class", "mb-0")], [text(chapter.name)])),
+ render_html(h(
+ "ul",
+ [],
+ list.map(chapter.lessons, fn(lesson) {
+ h("li", [], [
+ h("a", [#("href", lesson.path)], [
+ lesson.name
+ |> string.replace("-", " ")
+ |> string.capitalise
+ |> text,
+ ]),
+ ])
+ }),
+ )),
+ ])
+}
+
+fn render_html(html: htmb.Html) -> String {
+ html
+ |> htmb.render
+ |> string_builder.to_string
+}
+
+fn index_list_html(chapters: List(Chapter)) -> String {
+ h("h2", [], [text("Table of contents")])
+ |> render_html
+ |> string.append(
+ chapters
+ |> list.map(index_chapter_html)
+ |> string.join("\n"),
+ )
+}
+
+fn write_lesson(lesson: Lesson) -> snag.Result(Nil) {
+ let path = public <> lesson.path
use _ <- result.try(
simplifile.create_directory_all(path)
|> file_error("Failed to make " <> path),
)
let path = path <> "/index.html"
- simplifile.write(to: path, contents: page_html(page))
+ simplifile.write(to: path, contents: lesson_html(lesson))
|> file_error("Failed to write page " <> path)
}
-fn add_previous_next(
- rest: List(Page),
- acc: List(Page),
+fn add_prev_next(
+ rest: List(Chapter),
+ acc: List(Chapter),
previous: Option(String),
-) -> List(Page) {
+) -> List(Chapter) {
case rest {
+ [chapter1, Chapter(lessons: [next, ..], ..) as chapter2, ..rest] -> {
+ let lessons = chapter1.lessons
+ let #(lessons, previous) =
+ add_prev_next_for_chapter(lessons, [], previous, Some(next.path))
+ let chapter1 = Chapter(..chapter1, lessons: lessons)
+ add_prev_next([chapter2, ..rest], [chapter1, ..acc], previous)
+ }
+
+ [chapter, ..rest] -> {
+ let lessons = chapter.lessons
+ let #(lessons, previous) =
+ add_prev_next_for_chapter(lessons, [], previous, None)
+ let chapter = Chapter(..chapter, lessons: lessons)
+ add_prev_next(rest, [chapter, ..acc], previous)
+ }
+
[] -> list.reverse(acc)
- [page, next, ..rest] -> {
- let page = Page(..page, previous: previous, next: Some(next.path))
- add_previous_next([next, ..rest], [page, ..acc], Some(page.path))
+ }
+}
+
+fn add_prev_next_for_chapter(
+ rest: List(Lesson),
+ acc: List(Lesson),
+ previous: Option(String),
+ last: Option(String),
+) -> #(List(Lesson), Option(String)) {
+ case rest {
+ [lesson1, lesson2, ..rest] -> {
+ let next = Some(lesson2.path)
+ let lesson = Lesson(..lesson1, previous: previous, next: next)
+ let rest = [lesson2, ..rest]
+ add_prev_next_for_chapter(rest, [lesson, ..acc], Some(lesson.path), last)
}
- [page, ..rest] -> {
- let page = Page(..page, previous: previous, next: None)
- add_previous_next(rest, [page, ..acc], Some(page.path))
+ [lesson, ..rest] -> {
+ let lesson = Lesson(..lesson, previous: previous, next: last)
+ add_prev_next_for_chapter(rest, [lesson, ..acc], Some(lesson.path), last)
}
+ [] -> #(list.reverse(acc), previous)
}
}
@@ -353,7 +413,7 @@ fn file_error(
}
}
-fn page_html(page: Page) -> String {
+fn lesson_html(page: Lesson) -> String {
let navlink = fn(name, link) {
case link {
None -> h("span", [], [text(name)])