// 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.
/// }
/// }
///
/// assert fact(5) == 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
///
/// pub fn example() {
/// let factorial =
/// funtil.fix(fn(factorial, x) {
/// case x {
/// 0 -> 1
/// x -> x * factorial(x - 1)
/// }
/// })
///
/// assert fact(5) == 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) }
}
/// Compose two functions together, where the output of the first function is
/// passed as the input to the second function. This is known as _composition_
/// and can be a way to write code in a "point-free" style where you don't
/// explicitly mention function arguments.
///
/// ```gleam
/// import funtil
/// import gleam/list
/// import gleam/string
///
/// fn example() {
/// let shouty_names =
/// list.map(
/// ["yoshie", "danielle", "marniek"],
/// string.uppercase |> funtil.then(string.append(_, "!"))
/// )
///
/// assert shouty_names == ["YOSHIE!", "DANIELLE!", "MARNIEK!"]
/// }
/// ```
///
/// 🚨 Gleam intentionally doesn't have an operator for function composition. In
/// other languages this is often represented as `.` or `>>`. Point-free programming
/// can be a useful tool but it can also make code harder to understand, hold with
/// care!
///
pub fn then(first: fn(a) -> b, do second: fn(b) -> c) -> fn(a) -> c {
fn(a) { first(a) |> second }
}