// TYPES -----------------------------------------------------------------------
/// This type is used to represent a value that can never happen. What does that
/// mean exactly?
///
/// - A `Bool` is a type that has two values: `True` and `False`.
/// - `Nil` is a type that has one value: `Nil`.
/// - `Never` is a type that has zero values: it's impossible to construct!
///
/// Why this type is useful is a bit of a mind-bender, but it's a useful tool to
/// have in your toolbox. For example, if you have a function that returns a
/// `Result(Int, Never)` then you *know* that it will always return an `Int` and
/// it is safe to use `let assert` to unwrap the value.
///
pub opaque type Never {
JustOneMore(Never)
}
// MANIPULATIONS ---------------------------------------------------------------
/// If you've got a `Never` somewhere in a type it can be a bit of a problem if
/// you need something else. Just like [`Never`](#Never) is a type that can never
/// be constructed, `never` is a function that can never be called.
///
/// To take our `Result(Int, Never)` example from above, what if we want to
/// pass that value into a function that expects a `Result(Int, String)`? As it
/// stands, the types don't match up, but because we know that we can never have
/// an error, we can use `never` to pretend to convert it into a `String`:
///
/// ```gleam
/// import funtil.{Never, never}
/// import gleam/io
/// import gleam/result
///
/// fn log_error(result: Result(a, String)) -> Result(a, String) {
/// case result {
/// Ok(a) -> Nil
/// Error(message) -> io.println(message)
/// }
///
/// result
/// }
///
/// fn example() {
/// let val: Result(Int, Never) = Ok(42)
///
/// val
/// |> result.map_error(never)
/// |> log_error
/// }
/// ```
///
pub fn never(val: Never) -> a {
case val {
JustOneMore(x) -> never(x)
}
}
// CONVERSIONS -----------------------------------------------------------------
/// Take any value and replace it with `Nil`. This can be a nicer way of using
/// value-producing functions in places where you only care about their side
/// effects.
///
pub fn void(_: a) -> Nil {
Nil
}
// UTILS -----------------------------------------------------------------------
/// Gleam's type system does not support recursive `let`-bound functions, even
/// though they are theoretically possible. The `fix` combinator is a sneaky way
/// around this limitation by making the recursive function a parameter of
/// itself.
///
/// Sound a bit too magical? Let's first take a look at what happens if we try
/// to write a recursive `let`-bound function in Gleam:
///
/// ```gleam
/// pub fn example() {
/// let factorial = fn(x) {
/// case x {
/// 0 -> 1
/// x -> x * factorial(x - 1)
/// // ^^^^^^^^^ The name `factorial` is not in scope here.
/// }
/// }
///
/// fact(5)
/// |> should.equal(120)
/// }
/// ```
///
/// We get a compile error because the name `factorial` is not in scope inside
/// the function body. What does it look like if we try to use `fix`?
///
/// ```gleam
/// import funtil.{fix}
///
/// pub fn example() {
/// let factorial =
/// fix(fn(factorial, x) {
/// case x {
/// 0 -> 1
/// x -> x * factorial(x - 1)
/// }
/// })
///
/// fact(5)
/// |> should.equal(120)
/// }
/// ```
///
/// 🚨 Gleam is designed with this limitation to encourage you to pull things out
/// of `let` bindings when they get too complex. If you find yourself reaching for
/// `fix`, consider if there's a clearer way to solve your problem.
///
pub fn fix(f) {
fn(x) { f(fix(f), x) }
}
/// A version of the [`fix`](#fix) util for functions that take two arguments.
///
/// 🚨 Gleam is designed with this limitation to encourage you to pull things out
/// of `let` bindings when they get too complex. If you find yourself reaching for
/// `fix2`, consider if there's a clearer way to solve your problem.
///
pub fn fix2(f) {
fn(x, y) { f(fix2(f), x, y) }
}
/// A version of the [`fix`](#fix) util for functions that take three arguments.
///
/// 🚨 Gleam is designed with this limitation to encourage you to pull things out
/// of `let` bindings when they get too complex. If you find yourself reaching for
/// `fix3`, consider if there's a clearer way to solve your problem.
///
pub fn fix3(f) {
fn(x, y, z) { f(fix3(f), x, y, z) }
}