on
Ergonomic guards for the core gleam types to apply with the <- use
syntax.
gleam add on@1
To be compared with the given package. While given uses truthiness-based semantics, on uses a “variantyness” -based approach.
To wit, the general given API call has the form and usage:
// 'given' package
fn statement_of_truthiness_about_thing(
thing: Thing,
else_return f1: fn(falsiness_payload) -> ..., // what to compute if statement does not hold
return f2: fn(truthiness_payload) -> ..., // what to compute if statement holds
)
// usage
use truthiness_payload <- given.statement_of_truthiness_about_thing(
some_thing(),
what_to_do_with_falsiness_payload, // the 'f1' from above
)
...
While for on the general form and usage is:
// 'on' package
fn variant1_variant2_variant3(
thing: Thing,
on_variant1 f1: fn(variant1_payload) -> ...,
on_variant2 f1: fn(variant2_payload) -> ...,
on_variant3 f1: fn(variant3_payload) -> ...,
)
// usage
use variant3_payload <- on.variant1_variant2_variant3(
some_thing(),
what_to_do_with_variant1_payload,
what_to_do_with_variant2_payload,
)
...
Note that on expects values, not callbacks, for 0-ary variants. Use the lazy_
version of the API call (e.g., on.lazy_true_false
instead of on.true_false
) if eager evaluation is problematic. (E.g., expensive or side-effectful.)
Variants are elided when mapped to themselves. E.g., on.ok
is the specialized version of on.error_ok
for which the Error
-variant callback maps Error(b)
to Error(b)
. (I.e., on.ok
is isomorphic to result.try
.)
Example 1
import gleam/io
import gleam/string
import on
import simplifile
fn read_file(path: String) -> Result(String, String) {
simplifile.read(path)
|> result.map_error(
// map the error to an 'Error(String)'
// one could also use 'on.error' instead of 'result.map_error'
// as the two functions are isomorphic:
fn(e) { Error("simplifile FileError: " <> string.inspect(e)) }
)
}
pub fn main() -> Nil {
use contents <- on.error_ok(
read_file("./sample.txt"),
on_error: fn(msg) { io.println("\n" <> msg) }
)
use first, rest <- on.lazy_empty_nonempty(
string.split(contents, "\n"),
on_empty: fn() { io.println("empty contents") },
)
use <- on.lazy_false_true(
string.trim(first) == "<!DOCTYPE html>",
on_false: fn() { io.println("expecting DOCTYPE in first line") },
)
use parse_tree <- on.error_ok(
parse_html(rest),
on_error: fn(e) { println("html parse error: " <> string.inspect(e)) },
)
// ...
}
Example 1
import gleam/float
import gleam/int
import gleam/option.{type Option, None, Some}
import gleam/string
import on
type CSSUnit {
PX
REM
EM
}
fn extract_css_unit(s: String) -> #(String, Option(CSSUnit)) {
use <- on.true_false(
string.ends_with(s, "rem"),
on_true: #(string.drop_end(s, 3), Some(REM)),
)
use <- on.true_false(
string.ends_with(s, "em"),
on_true: #(string.drop_end(s, 2), Some(EM)),
)
use <- on.true_false(
string.ends_with(s, "px"),
on_true: #(string.drop_end(s, 2), Some(PX)),
)
#(s, None)
}
fn parse_to_float(s: String) -> Result(Float, Nil) {
case float.parse(s), int.parse(s) {
Ok(number), _ -> Ok(number)
_, Ok(number) -> Ok(int.to_float(number))
_, _ -> Error(Nil)
}
}
pub fn parse_number_and_optional_css_unit(
s: String,
) -> Result(#(Float, Option(CSSUnit)), Nil) {
let #(before_unit, unit) = extract_css_unit(s)
use number <- on.ok(parse_to_float(before_unit))
Ok(#(number, unit))
}
Development
gleam run # Run the project
gleam test # Run the tests