-module(funtil). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/funtil.gleam"). -export([never/1, void/1, fix/1, fix2/1, fix3/1, then/2]). -export_type([never/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -opaque never() :: {just_one_more, never()}. -file("src/funtil.gleam", 53). ?DOC( " If you've got a `Never` somewhere in a type it can be a bit of a problem if\n" " you need something else. Just like [`Never`](#Never) is a type that can never\n" " be constructed, `never` is a function that can never be called.\n" "\n" " To take our `Result(Int, Never)` example from above, what if we want to\n" " pass that value into a function that expects a `Result(Int, String)`? As it\n" " stands, the types don't match up, but because we know that we can never have\n" " an error, we can use `never` to pretend to convert it into a `String`:\n" "\n" " ```gleam\n" " import funtil.{Never, never}\n" " import gleam/io\n" " import gleam/result\n" "\n" " fn log_error(result: Result(a, String)) -> Result(a, String) {\n" " case result {\n" " Ok(a) -> Nil\n" " Error(message) -> io.println(message)\n" " }\n" "\n" " result\n" " }\n" "\n" " fn example() {\n" " let val: Result(Int, Never) = Ok(42)\n" "\n" " val\n" " |> result.map_error(never)\n" " |> log_error\n" " }\n" " ```\n" ). -spec never(never()) -> any(). never(Val) -> case Val of {just_one_more, X} -> never(X) end. -file("src/funtil.gleam", 65). ?DOC( " Take any value and replace it with `Nil`. This can be a nicer way of using\n" " value-producing functions in places where you only care about their side\n" " effects.\n" ). -spec void(any()) -> nil. void(_) -> nil. -file("src/funtil.gleam", 116). ?DOC( " Gleam's type system does not support recursive `let`-bound functions, even\n" " though they are theoretically possible. The `fix` combinator is a sneaky way\n" " around this limitation by making the recursive function a parameter of\n" " itself.\n" "\n" " Sound a bit too magical? Let's first take a look at what happens if we try\n" " to write a recursive `let`-bound function in Gleam:\n" "\n" " ```gleam\n" " pub fn example() {\n" " let factorial = fn(x) {\n" " case x {\n" " 0 -> 1\n" " x -> x * factorial(x - 1)\n" " // ^^^^^^^^^ The name `factorial` is not in scope here.\n" " }\n" " }\n" "\n" " assert fact(5) == 120\n" " }\n" " ```\n" "\n" " We get a compile error because the name `factorial` is not in scope inside\n" " the function body. What does it look like if we try to use `fix`?\n" "\n" " ```gleam\n" " import funtil\n" "\n" " pub fn example() {\n" " let factorial =\n" " funtil.fix(fn(factorial, x) {\n" " case x {\n" " 0 -> 1\n" " x -> x * factorial(x - 1)\n" " }\n" " })\n" "\n" " assert fact(5) == 120\n" " }\n" " ```\n" "\n" " 🚨 Gleam is designed with this limitation to encourage you to pull things out\n" " of `let` bindings when they get too complex. If you find yourself reaching for\n" " `fix`, consider if there's a clearer way to solve your problem.\n" ). -spec fix(fun((fun((DUG) -> DUJ), DUG) -> DUJ)) -> fun((DUG) -> DUJ). fix(F) -> fun(X) -> F(fix(F), X) end. -file("src/funtil.gleam", 126). ?DOC( " A version of the [`fix`](#fix) util for functions that take two arguments.\n" "\n" " 🚨 Gleam is designed with this limitation to encourage you to pull things out\n" " of `let` bindings when they get too complex. If you find yourself reaching for\n" " `fix2`, consider if there's a clearer way to solve your problem.\n" ). -spec fix2(fun((fun((DUK, DUL) -> DUP), DUK, DUL) -> DUP)) -> fun((DUK, DUL) -> DUP). fix2(F) -> fun(X, Y) -> F(fix2(F), X, Y) end. -file("src/funtil.gleam", 136). ?DOC( " A version of the [`fix`](#fix) util for functions that take three arguments.\n" "\n" " 🚨 Gleam is designed with this limitation to encourage you to pull things out\n" " of `let` bindings when they get too complex. If you find yourself reaching for\n" " `fix3`, consider if there's a clearer way to solve your problem.\n" ). -spec fix3(fun((fun((DUQ, DUR, DUS) -> DUX), DUQ, DUR, DUS) -> DUX)) -> fun((DUQ, DUR, DUS) -> DUX). fix3(F) -> fun(X, Y, Z) -> F(fix3(F), X, Y, Z) end. -file("src/funtil.gleam", 166). ?DOC( " Compose two functions together, where the output of the first function is\n" " passed as the input to the second function. This is known as _composition_\n" " and can be a way to write code in a \"point-free\" style where you don't\n" " explicitly mention function arguments.\n" "\n" " ```gleam\n" " import funtil\n" " import gleam/list\n" " import gleam/string\n" "\n" " fn example() {\n" " let shouty_names =\n" " list.map(\n" " [\"yoshie\", \"danielle\", \"marniek\"],\n" " string.uppercase |> funtil.then(string.append(_, \"!\"))\n" " )\n" "\n" " assert shouty_names == [\"YOSHIE!\", \"DANIELLE!\", \"MARNIEK!\"]\n" " }\n" " ```\n" "\n" " 🚨 Gleam intentionally doesn't have an operator for function composition. In\n" " other languages this is often represented as `.` or `>>`. Point-free programming\n" " can be a useful tool but it can also make code harder to understand, hold with\n" " care!\n" ). -spec then(fun((DUC) -> DUD), fun((DUD) -> DUE)) -> fun((DUC) -> DUE). then(First, Second) -> fun(A) -> _pipe = First(A), Second(_pipe) end.