From c2dcbe0f25a3e8bd60a4ccf377bbdd47f4794871 Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Tue, 26 Mar 2024 13:57:31 +0000 Subject: Document opaque types Closes https://github.com/gleam-lang/language-tour/issues/63 --- .../lesson00_opaque_types/code.gleam | 26 +++++++++++++++ .../lesson00_opaque_types/en.html | 17 ++++++++++ .../lesson00_use/code.gleam | 38 ---------------------- .../lesson00_use/en.html | 31 ------------------ .../lesson01_use/code.gleam | 38 ++++++++++++++++++++++ .../lesson01_use/en.html | 31 ++++++++++++++++++ .../lesson01_use_sugar/code.gleam | 30 ----------------- .../lesson01_use_sugar/en.html | 30 ----------------- .../lesson02_todo/code.gleam | 7 ---- .../lesson02_todo/en.html | 14 -------- .../lesson02_use_sugar/code.gleam | 30 +++++++++++++++++ .../lesson02_use_sugar/en.html | 30 +++++++++++++++++ .../lesson03_panic/code.gleam | 15 --------- .../lesson03_panic/en.html | 11 ------- .../lesson03_todo/code.gleam | 7 ++++ .../lesson03_todo/en.html | 14 ++++++++ .../lesson04_let_assert/code.gleam | 16 --------- .../lesson04_let_assert/en.html | 15 --------- .../lesson04_panic/code.gleam | 15 +++++++++ .../lesson04_panic/en.html | 11 +++++++ .../lesson05_externals/code.gleam | 17 ---------- .../lesson05_externals/en.html | 25 -------------- .../lesson05_let_assert/code.gleam | 16 +++++++++ .../lesson05_let_assert/en.html | 15 +++++++++ .../lesson06_externals/code.gleam | 17 ++++++++++ .../lesson06_externals/en.html | 25 ++++++++++++++ .../lesson06_multi_target_externals/code.gleam | 11 ------- .../lesson06_multi_target_externals/en.html | 22 ------------- .../lesson07_external_gleam_fallbacks/code.gleam | 18 ---------- .../lesson07_external_gleam_fallbacks/en.html | 13 -------- .../lesson07_multi_target_externals/code.gleam | 11 +++++++ .../lesson07_multi_target_externals/en.html | 22 +++++++++++++ .../lesson08_external_gleam_fallbacks/code.gleam | 18 ++++++++++ .../lesson08_external_gleam_fallbacks/en.html | 13 ++++++++ 34 files changed, 356 insertions(+), 313 deletions(-) create mode 100644 src/content/chapter5_advanced_features/lesson00_opaque_types/code.gleam create mode 100644 src/content/chapter5_advanced_features/lesson00_opaque_types/en.html delete mode 100644 src/content/chapter5_advanced_features/lesson00_use/code.gleam delete mode 100644 src/content/chapter5_advanced_features/lesson00_use/en.html create mode 100644 src/content/chapter5_advanced_features/lesson01_use/code.gleam create mode 100644 src/content/chapter5_advanced_features/lesson01_use/en.html delete mode 100644 src/content/chapter5_advanced_features/lesson01_use_sugar/code.gleam delete mode 100644 src/content/chapter5_advanced_features/lesson01_use_sugar/en.html delete mode 100644 src/content/chapter5_advanced_features/lesson02_todo/code.gleam delete mode 100644 src/content/chapter5_advanced_features/lesson02_todo/en.html create mode 100644 src/content/chapter5_advanced_features/lesson02_use_sugar/code.gleam create mode 100644 src/content/chapter5_advanced_features/lesson02_use_sugar/en.html delete mode 100644 src/content/chapter5_advanced_features/lesson03_panic/code.gleam delete mode 100644 src/content/chapter5_advanced_features/lesson03_panic/en.html create mode 100644 src/content/chapter5_advanced_features/lesson03_todo/code.gleam create mode 100644 src/content/chapter5_advanced_features/lesson03_todo/en.html delete mode 100644 src/content/chapter5_advanced_features/lesson04_let_assert/code.gleam delete mode 100644 src/content/chapter5_advanced_features/lesson04_let_assert/en.html create mode 100644 src/content/chapter5_advanced_features/lesson04_panic/code.gleam create mode 100644 src/content/chapter5_advanced_features/lesson04_panic/en.html delete mode 100644 src/content/chapter5_advanced_features/lesson05_externals/code.gleam delete mode 100644 src/content/chapter5_advanced_features/lesson05_externals/en.html create mode 100644 src/content/chapter5_advanced_features/lesson05_let_assert/code.gleam create mode 100644 src/content/chapter5_advanced_features/lesson05_let_assert/en.html create mode 100644 src/content/chapter5_advanced_features/lesson06_externals/code.gleam create mode 100644 src/content/chapter5_advanced_features/lesson06_externals/en.html delete mode 100644 src/content/chapter5_advanced_features/lesson06_multi_target_externals/code.gleam delete mode 100644 src/content/chapter5_advanced_features/lesson06_multi_target_externals/en.html delete mode 100644 src/content/chapter5_advanced_features/lesson07_external_gleam_fallbacks/code.gleam delete mode 100644 src/content/chapter5_advanced_features/lesson07_external_gleam_fallbacks/en.html create mode 100644 src/content/chapter5_advanced_features/lesson07_multi_target_externals/code.gleam create mode 100644 src/content/chapter5_advanced_features/lesson07_multi_target_externals/en.html create mode 100644 src/content/chapter5_advanced_features/lesson08_external_gleam_fallbacks/code.gleam create mode 100644 src/content/chapter5_advanced_features/lesson08_external_gleam_fallbacks/en.html (limited to 'src') diff --git a/src/content/chapter5_advanced_features/lesson00_opaque_types/code.gleam b/src/content/chapter5_advanced_features/lesson00_opaque_types/code.gleam new file mode 100644 index 0000000..d116b42 --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson00_opaque_types/code.gleam @@ -0,0 +1,26 @@ +import gleam/io + +pub fn main() { + let positive = new(1) + let zero = new(0) + let negative = new(-1) + + io.debug(to_int(positive)) + io.debug(to_int(zero)) + io.debug(to_int(negative)) +} + +pub opaque type PositiveInt { + PositiveInt(inner: Int) +} + +pub fn new(i: Int) -> PositiveInt { + case i >= 0 { + True -> PositiveInt(i) + False -> PositiveInt(0) + } +} + +pub fn to_int(i: PositiveInt) -> Int { + i.inner +} diff --git a/src/content/chapter5_advanced_features/lesson00_opaque_types/en.html b/src/content/chapter5_advanced_features/lesson00_opaque_types/en.html new file mode 100644 index 0000000..dbd362f --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson00_opaque_types/en.html @@ -0,0 +1,17 @@ +

+ Opaque types are types where a custom type itself is public and can + be used by other modules, but the constructors for the type are private and + can only be used by the module that defines the type. This prevents other + modules from constructing or pattern matching on the type. +

+

+ This is useful for creating types with smart constructors. A smart + constructor is a function that constructs a value of a type, but is more + restrictive than if the programmer were to use one of the type's constructors + directly. This can be useful for ensuring that the type is used correctly. +

+

+ For example, this PositiveInt custom type is opaque, so if other + modules want to construct one they have to use the new function, + which ensures that the integer is positive. +

diff --git a/src/content/chapter5_advanced_features/lesson00_use/code.gleam b/src/content/chapter5_advanced_features/lesson00_use/code.gleam deleted file mode 100644 index e7b435b..0000000 --- a/src/content/chapter5_advanced_features/lesson00_use/code.gleam +++ /dev/null @@ -1,38 +0,0 @@ -import gleam/io -import gleam/result - -pub fn main() { - io.debug(without_use()) - io.debug(with_use()) -} - -pub fn without_use() { - result.try(get_username(), fn(username) { - result.try(get_password(), fn(password) { - result.map(log_in(username, password), fn(greeting) { - greeting <> ", " <> username - }) - }) - }) -} - -pub fn with_use() { - use username <- result.try(get_username()) - use password <- result.try(get_password()) - use greeting <- result.map(log_in(username, password)) - greeting <> ", " <> username -} - -// Here are some pretend functions for this example: - -fn get_username() { - Ok("alice") -} - -fn get_password() { - Ok("hunter2") -} - -fn log_in(_username: String, _password: String) { - Ok("Welcome") -} diff --git a/src/content/chapter5_advanced_features/lesson00_use/en.html b/src/content/chapter5_advanced_features/lesson00_use/en.html deleted file mode 100644 index b8e2641..0000000 --- a/src/content/chapter5_advanced_features/lesson00_use/en.html +++ /dev/null @@ -1,31 +0,0 @@ -

- Gleam lacks exceptions, macros, type classes, early returns, and a variety of - other features, instead going all-in with just first-class-functions and - pattern matching. This makes Gleam code easier to understand, but it can - sometimes result in excessive indentation. -

-

- Gleam's use expression helps out here by enabling us to write code that uses - callbacks in an unindented style, as shown in the code window. -

- -

- The higher order function being called goes on the right hand side of the - <- operator. It must take a callback function as its final - argument. -

-

- The argument names for the callback function go on the left hand side of the - <- operator. The function can take any number of arguments, - including zero. -

-

- All the remaining code in the enclosing {} block - becomes the body of the callback function. -

-

- This is a very capable and useful feature, but excessive application of - use may result in code that is unclear otherwise, especially to - beginners. Often using the regular function call syntax will result in more - approachable code! -

diff --git a/src/content/chapter5_advanced_features/lesson01_use/code.gleam b/src/content/chapter5_advanced_features/lesson01_use/code.gleam new file mode 100644 index 0000000..e7b435b --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson01_use/code.gleam @@ -0,0 +1,38 @@ +import gleam/io +import gleam/result + +pub fn main() { + io.debug(without_use()) + io.debug(with_use()) +} + +pub fn without_use() { + result.try(get_username(), fn(username) { + result.try(get_password(), fn(password) { + result.map(log_in(username, password), fn(greeting) { + greeting <> ", " <> username + }) + }) + }) +} + +pub fn with_use() { + use username <- result.try(get_username()) + use password <- result.try(get_password()) + use greeting <- result.map(log_in(username, password)) + greeting <> ", " <> username +} + +// Here are some pretend functions for this example: + +fn get_username() { + Ok("alice") +} + +fn get_password() { + Ok("hunter2") +} + +fn log_in(_username: String, _password: String) { + Ok("Welcome") +} diff --git a/src/content/chapter5_advanced_features/lesson01_use/en.html b/src/content/chapter5_advanced_features/lesson01_use/en.html new file mode 100644 index 0000000..b8e2641 --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson01_use/en.html @@ -0,0 +1,31 @@ +

+ Gleam lacks exceptions, macros, type classes, early returns, and a variety of + other features, instead going all-in with just first-class-functions and + pattern matching. This makes Gleam code easier to understand, but it can + sometimes result in excessive indentation. +

+

+ Gleam's use expression helps out here by enabling us to write code that uses + callbacks in an unindented style, as shown in the code window. +

+ +

+ The higher order function being called goes on the right hand side of the + <- operator. It must take a callback function as its final + argument. +

+

+ The argument names for the callback function go on the left hand side of the + <- operator. The function can take any number of arguments, + including zero. +

+

+ All the remaining code in the enclosing {} block + becomes the body of the callback function. +

+

+ This is a very capable and useful feature, but excessive application of + use may result in code that is unclear otherwise, especially to + beginners. Often using the regular function call syntax will result in more + approachable code! +

diff --git a/src/content/chapter5_advanced_features/lesson01_use_sugar/code.gleam b/src/content/chapter5_advanced_features/lesson01_use_sugar/code.gleam deleted file mode 100644 index 6c5ccaa..0000000 --- a/src/content/chapter5_advanced_features/lesson01_use_sugar/code.gleam +++ /dev/null @@ -1,30 +0,0 @@ -import gleam/io -import gleam/result - -pub fn main() { - let x = { - use username <- result.try(get_username()) - use password <- result.try(get_password()) - use greeting <- result.map(log_in(username, password)) - greeting <> ", " <> username - } - - case x { - Ok(greeting) -> io.println(greeting) - Error(error) -> io.println("ERROR:" <> error) - } -} - -// Here are some pretend functions for this example: - -fn get_username() { - Ok("alice") -} - -fn get_password() { - Ok("hunter2") -} - -fn log_in(_username: String, _password: String) { - Ok("Welcome") -} diff --git a/src/content/chapter5_advanced_features/lesson01_use_sugar/en.html b/src/content/chapter5_advanced_features/lesson01_use_sugar/en.html deleted file mode 100644 index e28c843..0000000 --- a/src/content/chapter5_advanced_features/lesson01_use_sugar/en.html +++ /dev/null @@ -1,30 +0,0 @@ -

- The use expression is syntactic sugar for a regular function call - and an anonymous function. -

- -

This code:

-
-use a, b <- my_function
-next(a)
-next(b)
-
- -

Expands into this code:

-
-my_function(fn(a, b) {
-  next(a)
-  next(b)
-})
-
- -

- To ensure that your use code works and is as understandable as - possible, the right-hand-side ideally should be a function call rather than a - pipeline or other expression, which is typically more difficult to read. -

- -

- use is an expression like everything else in Gleam, so it can be - placed within blocks. -

diff --git a/src/content/chapter5_advanced_features/lesson02_todo/code.gleam b/src/content/chapter5_advanced_features/lesson02_todo/code.gleam deleted file mode 100644 index d5abe8f..0000000 --- a/src/content/chapter5_advanced_features/lesson02_todo/code.gleam +++ /dev/null @@ -1,7 +0,0 @@ -pub fn main() { - todo as "I haven't written this code yet!" -} - -pub fn todo_without_reason() { - todo -} diff --git a/src/content/chapter5_advanced_features/lesson02_todo/en.html b/src/content/chapter5_advanced_features/lesson02_todo/en.html deleted file mode 100644 index 4a2c433..0000000 --- a/src/content/chapter5_advanced_features/lesson02_todo/en.html +++ /dev/null @@ -1,14 +0,0 @@ -

- The todo keyword is used to specify that some code is not yet - implemented. -

-

- The as "some string" is optional, though you may wish to include - the message if you have more than one code block marked as - todo in your code. -

-

- When used the Gleam compiler will print a warning to remind you the code is - unfinished, and if the code is run then the program will crash with the given - message. -

diff --git a/src/content/chapter5_advanced_features/lesson02_use_sugar/code.gleam b/src/content/chapter5_advanced_features/lesson02_use_sugar/code.gleam new file mode 100644 index 0000000..6c5ccaa --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson02_use_sugar/code.gleam @@ -0,0 +1,30 @@ +import gleam/io +import gleam/result + +pub fn main() { + let x = { + use username <- result.try(get_username()) + use password <- result.try(get_password()) + use greeting <- result.map(log_in(username, password)) + greeting <> ", " <> username + } + + case x { + Ok(greeting) -> io.println(greeting) + Error(error) -> io.println("ERROR:" <> error) + } +} + +// Here are some pretend functions for this example: + +fn get_username() { + Ok("alice") +} + +fn get_password() { + Ok("hunter2") +} + +fn log_in(_username: String, _password: String) { + Ok("Welcome") +} diff --git a/src/content/chapter5_advanced_features/lesson02_use_sugar/en.html b/src/content/chapter5_advanced_features/lesson02_use_sugar/en.html new file mode 100644 index 0000000..e28c843 --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson02_use_sugar/en.html @@ -0,0 +1,30 @@ +

+ The use expression is syntactic sugar for a regular function call + and an anonymous function. +

+ +

This code:

+
+use a, b <- my_function
+next(a)
+next(b)
+
+ +

Expands into this code:

+
+my_function(fn(a, b) {
+  next(a)
+  next(b)
+})
+
+ +

+ To ensure that your use code works and is as understandable as + possible, the right-hand-side ideally should be a function call rather than a + pipeline or other expression, which is typically more difficult to read. +

+ +

+ use is an expression like everything else in Gleam, so it can be + placed within blocks. +

diff --git a/src/content/chapter5_advanced_features/lesson03_panic/code.gleam b/src/content/chapter5_advanced_features/lesson03_panic/code.gleam deleted file mode 100644 index fce9d66..0000000 --- a/src/content/chapter5_advanced_features/lesson03_panic/code.gleam +++ /dev/null @@ -1,15 +0,0 @@ -import gleam/io - -pub fn main() { - print_score(10) - print_score(100_000) - print_score(-1) -} - -pub fn print_score(score: Int) { - case score { - score if score > 1000 -> io.println("High score!") - score if score > 0 -> io.println("Still working on it") - _ -> panic as "Scores should never be negative!" - } -} diff --git a/src/content/chapter5_advanced_features/lesson03_panic/en.html b/src/content/chapter5_advanced_features/lesson03_panic/en.html deleted file mode 100644 index c54c217..0000000 --- a/src/content/chapter5_advanced_features/lesson03_panic/en.html +++ /dev/null @@ -1,11 +0,0 @@ -

- The panic keyword is similar to the todo keyword, - but it is used to crash the program when the program has reached a point that - should never be reached. -

-

- This keyword should almost never be used! It may be useful in initial - prototypes and scripts, but its use in a library or production application is - a sign that the design could be improved. With well designed types the type - system can typically be used to make these invalid states unrepresentable. -

diff --git a/src/content/chapter5_advanced_features/lesson03_todo/code.gleam b/src/content/chapter5_advanced_features/lesson03_todo/code.gleam new file mode 100644 index 0000000..d5abe8f --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson03_todo/code.gleam @@ -0,0 +1,7 @@ +pub fn main() { + todo as "I haven't written this code yet!" +} + +pub fn todo_without_reason() { + todo +} diff --git a/src/content/chapter5_advanced_features/lesson03_todo/en.html b/src/content/chapter5_advanced_features/lesson03_todo/en.html new file mode 100644 index 0000000..4a2c433 --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson03_todo/en.html @@ -0,0 +1,14 @@ +

+ The todo keyword is used to specify that some code is not yet + implemented. +

+

+ The as "some string" is optional, though you may wish to include + the message if you have more than one code block marked as + todo in your code. +

+

+ When used the Gleam compiler will print a warning to remind you the code is + unfinished, and if the code is run then the program will crash with the given + message. +

diff --git a/src/content/chapter5_advanced_features/lesson04_let_assert/code.gleam b/src/content/chapter5_advanced_features/lesson04_let_assert/code.gleam deleted file mode 100644 index 2ba907a..0000000 --- a/src/content/chapter5_advanced_features/lesson04_let_assert/code.gleam +++ /dev/null @@ -1,16 +0,0 @@ -import gleam/io - -pub fn main() { - let a = unsafely_get_first_element([123]) - io.debug(a) - - let b = unsafely_get_first_element([]) - io.debug(b) -} - -pub fn unsafely_get_first_element(items: List(a)) -> a { - // This will panic if the list is empty. - // A regular `let` would not permit this partial pattern - let assert [first, ..] = items - first -} diff --git a/src/content/chapter5_advanced_features/lesson04_let_assert/en.html b/src/content/chapter5_advanced_features/lesson04_let_assert/en.html deleted file mode 100644 index 29fd06c..0000000 --- a/src/content/chapter5_advanced_features/lesson04_let_assert/en.html +++ /dev/null @@ -1,15 +0,0 @@ -

- let assert is the final way to intentionally crash your Gleam - program. It is similar to the panic keyword in that it crashes - when the program has reached a point that should never be reached. -

-

- let assert is similar to let in that it is a way to - assign values to variables, but it is different in that the pattern can be - partial. The pattern does not need to match every possible value of the - type being assigned. -

-

- Like panic this feature should be used sparingly, and likely not - at all in libraries. -

diff --git a/src/content/chapter5_advanced_features/lesson04_panic/code.gleam b/src/content/chapter5_advanced_features/lesson04_panic/code.gleam new file mode 100644 index 0000000..fce9d66 --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson04_panic/code.gleam @@ -0,0 +1,15 @@ +import gleam/io + +pub fn main() { + print_score(10) + print_score(100_000) + print_score(-1) +} + +pub fn print_score(score: Int) { + case score { + score if score > 1000 -> io.println("High score!") + score if score > 0 -> io.println("Still working on it") + _ -> panic as "Scores should never be negative!" + } +} diff --git a/src/content/chapter5_advanced_features/lesson04_panic/en.html b/src/content/chapter5_advanced_features/lesson04_panic/en.html new file mode 100644 index 0000000..c54c217 --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson04_panic/en.html @@ -0,0 +1,11 @@ +

+ The panic keyword is similar to the todo keyword, + but it is used to crash the program when the program has reached a point that + should never be reached. +

+

+ This keyword should almost never be used! It may be useful in initial + prototypes and scripts, but its use in a library or production application is + a sign that the design could be improved. With well designed types the type + system can typically be used to make these invalid states unrepresentable. +

diff --git a/src/content/chapter5_advanced_features/lesson05_externals/code.gleam b/src/content/chapter5_advanced_features/lesson05_externals/code.gleam deleted file mode 100644 index 1101b82..0000000 --- a/src/content/chapter5_advanced_features/lesson05_externals/code.gleam +++ /dev/null @@ -1,17 +0,0 @@ -import gleam/io - -// A type with no Gleam constructors -pub type DateTime - -// An external function that creates an instance of the type -@external(javascript, "./my_package_ffi.mjs", "now") -pub fn now() -> DateTime - -// The `now` function in `./my_package_ffi.mjs` looks like this: -// export function now() { -// return new Date(); -// } - -pub fn main() { - io.debug(now()) -} diff --git a/src/content/chapter5_advanced_features/lesson05_externals/en.html b/src/content/chapter5_advanced_features/lesson05_externals/en.html deleted file mode 100644 index 8815fa7..0000000 --- a/src/content/chapter5_advanced_features/lesson05_externals/en.html +++ /dev/null @@ -1,25 +0,0 @@ -

- Sometimes in our projects we want to use code written in other languages, most - commonly Erlang and JavaScript, depending on which runtime is being used. - Gleam's external functions and external types allow us to - import and use this non-Gleam code. -

-

- An external type is one that has no constructors. Gleam doesn't know what - shape it has or how to create one, it only knows that it exists. -

-

- An external function is one that has the @external attribute on - it, directing the compiler to use the specified module function as the - implementation, instead of Gleam code. -

-

- The compiler can't tell the types of functions written in other languages, so - when the external attribute is given type annotations must be provided. Gleam - trusts that the type given is correct so an inaccurate type annotation can - result in unexpected behaviour and crashes at runtime. Be careful! -

-

- External functions are useful but should be used sparingly. Prefer to write - Gleam code where possible. -

diff --git a/src/content/chapter5_advanced_features/lesson05_let_assert/code.gleam b/src/content/chapter5_advanced_features/lesson05_let_assert/code.gleam new file mode 100644 index 0000000..2ba907a --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson05_let_assert/code.gleam @@ -0,0 +1,16 @@ +import gleam/io + +pub fn main() { + let a = unsafely_get_first_element([123]) + io.debug(a) + + let b = unsafely_get_first_element([]) + io.debug(b) +} + +pub fn unsafely_get_first_element(items: List(a)) -> a { + // This will panic if the list is empty. + // A regular `let` would not permit this partial pattern + let assert [first, ..] = items + first +} diff --git a/src/content/chapter5_advanced_features/lesson05_let_assert/en.html b/src/content/chapter5_advanced_features/lesson05_let_assert/en.html new file mode 100644 index 0000000..29fd06c --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson05_let_assert/en.html @@ -0,0 +1,15 @@ +

+ let assert is the final way to intentionally crash your Gleam + program. It is similar to the panic keyword in that it crashes + when the program has reached a point that should never be reached. +

+

+ let assert is similar to let in that it is a way to + assign values to variables, but it is different in that the pattern can be + partial. The pattern does not need to match every possible value of the + type being assigned. +

+

+ Like panic this feature should be used sparingly, and likely not + at all in libraries. +

diff --git a/src/content/chapter5_advanced_features/lesson06_externals/code.gleam b/src/content/chapter5_advanced_features/lesson06_externals/code.gleam new file mode 100644 index 0000000..1101b82 --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson06_externals/code.gleam @@ -0,0 +1,17 @@ +import gleam/io + +// A type with no Gleam constructors +pub type DateTime + +// An external function that creates an instance of the type +@external(javascript, "./my_package_ffi.mjs", "now") +pub fn now() -> DateTime + +// The `now` function in `./my_package_ffi.mjs` looks like this: +// export function now() { +// return new Date(); +// } + +pub fn main() { + io.debug(now()) +} diff --git a/src/content/chapter5_advanced_features/lesson06_externals/en.html b/src/content/chapter5_advanced_features/lesson06_externals/en.html new file mode 100644 index 0000000..8815fa7 --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson06_externals/en.html @@ -0,0 +1,25 @@ +

+ Sometimes in our projects we want to use code written in other languages, most + commonly Erlang and JavaScript, depending on which runtime is being used. + Gleam's external functions and external types allow us to + import and use this non-Gleam code. +

+

+ An external type is one that has no constructors. Gleam doesn't know what + shape it has or how to create one, it only knows that it exists. +

+

+ An external function is one that has the @external attribute on + it, directing the compiler to use the specified module function as the + implementation, instead of Gleam code. +

+

+ The compiler can't tell the types of functions written in other languages, so + when the external attribute is given type annotations must be provided. Gleam + trusts that the type given is correct so an inaccurate type annotation can + result in unexpected behaviour and crashes at runtime. Be careful! +

+

+ External functions are useful but should be used sparingly. Prefer to write + Gleam code where possible. +

diff --git a/src/content/chapter5_advanced_features/lesson06_multi_target_externals/code.gleam b/src/content/chapter5_advanced_features/lesson06_multi_target_externals/code.gleam deleted file mode 100644 index b62a735..0000000 --- a/src/content/chapter5_advanced_features/lesson06_multi_target_externals/code.gleam +++ /dev/null @@ -1,11 +0,0 @@ -import gleam/io - -pub type DateTime - -@external(erlang, "calendar", "local_time") -@external(javascript, "./my_package_ffi.mjs", "now") -pub fn now() -> DateTime - -pub fn main() { - io.debug(now()) -} diff --git a/src/content/chapter5_advanced_features/lesson06_multi_target_externals/en.html b/src/content/chapter5_advanced_features/lesson06_multi_target_externals/en.html deleted file mode 100644 index 6e02d36..0000000 --- a/src/content/chapter5_advanced_features/lesson06_multi_target_externals/en.html +++ /dev/null @@ -1,22 +0,0 @@ -

- Multiple external implementations can be specified for the same function, - enabling the function to work on both Erlang and JavaScript. -

-

- If a function doesn't have an implementation for the currently compiled-for - target then the compiler will return an error. -

-

- You should try to implement functions for all targets, but this isn't always - possible due to incompatibilities in how IO and concurreny works in Erlang and - JavaScript. With Erlang concurrent IO is handled transparently by the runtime, - while in JavaScript concurrent IO requires the use of promises or callbacks. - If your code uses the Erlang style it is typically not possible to implement - in JavaScript, while if callbacks are used then it won't be compatible with - most Gleam and Erlang code as it forces any code that calls the function to - also use callbacks. -

-

- Libraries that make use of concurrent IO will typically have to decide whether - they support Erlang or JavaScript, and document this in their README. -

diff --git a/src/content/chapter5_advanced_features/lesson07_external_gleam_fallbacks/code.gleam b/src/content/chapter5_advanced_features/lesson07_external_gleam_fallbacks/code.gleam deleted file mode 100644 index a97b8fc..0000000 --- a/src/content/chapter5_advanced_features/lesson07_external_gleam_fallbacks/code.gleam +++ /dev/null @@ -1,18 +0,0 @@ -import gleam/io - -@external(erlang, "lists", "reverse") -pub fn reverse_list(items: List(e)) -> List(e) { - tail_recursive_reverse(items, []) -} - -fn tail_recursive_reverse(items: List(e), reversed: List(e)) -> List(e) { - case items { - [] -> reversed - [first, ..rest] -> tail_recursive_reverse(rest, [first, ..reversed]) - } -} - -pub fn main() { - io.debug(reverse_list([1, 2, 3, 4, 5])) - io.debug(reverse_list(["a", "b", "c", "d", "e"])) -} diff --git a/src/content/chapter5_advanced_features/lesson07_external_gleam_fallbacks/en.html b/src/content/chapter5_advanced_features/lesson07_external_gleam_fallbacks/en.html deleted file mode 100644 index 243c7ea..0000000 --- a/src/content/chapter5_advanced_features/lesson07_external_gleam_fallbacks/en.html +++ /dev/null @@ -1,13 +0,0 @@ -

- It's possible for a function to have both a Gleam implementation and an - external implementation. If there exists an external implementation for the - currently compiled-for target then it will be used, otherwise the Gleam - implementation is used. -

-

- This may be useful if you have a function that can be implemented in Gleam, - but there is an optimised implementation that can be used for one target. For - example, the Erlang virtual machine has a built-in list reverse function that - is implemented in native code. The code here uses this implementation when - running on Erlang, as it is then available. -

diff --git a/src/content/chapter5_advanced_features/lesson07_multi_target_externals/code.gleam b/src/content/chapter5_advanced_features/lesson07_multi_target_externals/code.gleam new file mode 100644 index 0000000..b62a735 --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson07_multi_target_externals/code.gleam @@ -0,0 +1,11 @@ +import gleam/io + +pub type DateTime + +@external(erlang, "calendar", "local_time") +@external(javascript, "./my_package_ffi.mjs", "now") +pub fn now() -> DateTime + +pub fn main() { + io.debug(now()) +} diff --git a/src/content/chapter5_advanced_features/lesson07_multi_target_externals/en.html b/src/content/chapter5_advanced_features/lesson07_multi_target_externals/en.html new file mode 100644 index 0000000..6e02d36 --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson07_multi_target_externals/en.html @@ -0,0 +1,22 @@ +

+ Multiple external implementations can be specified for the same function, + enabling the function to work on both Erlang and JavaScript. +

+

+ If a function doesn't have an implementation for the currently compiled-for + target then the compiler will return an error. +

+

+ You should try to implement functions for all targets, but this isn't always + possible due to incompatibilities in how IO and concurreny works in Erlang and + JavaScript. With Erlang concurrent IO is handled transparently by the runtime, + while in JavaScript concurrent IO requires the use of promises or callbacks. + If your code uses the Erlang style it is typically not possible to implement + in JavaScript, while if callbacks are used then it won't be compatible with + most Gleam and Erlang code as it forces any code that calls the function to + also use callbacks. +

+

+ Libraries that make use of concurrent IO will typically have to decide whether + they support Erlang or JavaScript, and document this in their README. +

diff --git a/src/content/chapter5_advanced_features/lesson08_external_gleam_fallbacks/code.gleam b/src/content/chapter5_advanced_features/lesson08_external_gleam_fallbacks/code.gleam new file mode 100644 index 0000000..a97b8fc --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson08_external_gleam_fallbacks/code.gleam @@ -0,0 +1,18 @@ +import gleam/io + +@external(erlang, "lists", "reverse") +pub fn reverse_list(items: List(e)) -> List(e) { + tail_recursive_reverse(items, []) +} + +fn tail_recursive_reverse(items: List(e), reversed: List(e)) -> List(e) { + case items { + [] -> reversed + [first, ..rest] -> tail_recursive_reverse(rest, [first, ..reversed]) + } +} + +pub fn main() { + io.debug(reverse_list([1, 2, 3, 4, 5])) + io.debug(reverse_list(["a", "b", "c", "d", "e"])) +} diff --git a/src/content/chapter5_advanced_features/lesson08_external_gleam_fallbacks/en.html b/src/content/chapter5_advanced_features/lesson08_external_gleam_fallbacks/en.html new file mode 100644 index 0000000..243c7ea --- /dev/null +++ b/src/content/chapter5_advanced_features/lesson08_external_gleam_fallbacks/en.html @@ -0,0 +1,13 @@ +

+ It's possible for a function to have both a Gleam implementation and an + external implementation. If there exists an external implementation for the + currently compiled-for target then it will be used, otherwise the Gleam + implementation is used. +

+

+ This may be useful if you have a function that can be implemented in Gleam, + but there is an optimised implementation that can be used for one target. For + example, the Erlang virtual machine has a built-in list reverse function that + is implemented in native code. The code here uses this implementation when + running on Erlang, as it is then available. +

-- cgit v1.2.3