From d7fa4d65ecae956c0676442ca6f775c3075adace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Rowicki?= <35342116+radrow@users.noreply.github.com> Date: Tue, 25 Feb 2020 12:56:51 +0100 Subject: [PATCH] More comments in stdlib (#237) --- priv/stdlib/Frac.aes | 21 ++++++++++++---- priv/stdlib/Func.aes | 51 +++++++++++++++++++++++++++++++-------- priv/stdlib/List.aes | 55 +++++++++++++++++++++++++++++++++--------- priv/stdlib/Option.aes | 22 +++++++++++++++-- priv/stdlib/Pair.aes | 6 +++++ priv/stdlib/Triple.aes | 12 +++++++++ 6 files changed, 139 insertions(+), 28 deletions(-) diff --git a/priv/stdlib/Frac.aes b/priv/stdlib/Frac.aes index 27ad531..1fdebb5 100644 --- a/priv/stdlib/Frac.aes +++ b/priv/stdlib/Frac.aes @@ -7,7 +7,10 @@ namespace Frac = datatype frac = Pos(int, int) | Zero | Neg(int, int) - // Checks if internal representation is correct. Numerator and denominator must be positive. +/** Checks if the internal representation is correct. + * Numerator and denominator must be positive. + * Exposed for debug purposes + */ function is_sane(f : frac) : bool = switch(f) Pos(n, d) => n > 0 && d > 0 Zero => true @@ -38,7 +41,8 @@ namespace Frac = Neg(n, d) => String.concat("-", to_str(Pos(n, d))) Zero => "0" - // Reduce fraction to normal form +/** Reduce fraction to normal form + */ function simplify(f : frac) : frac = switch(f) Neg(n, d) => @@ -49,6 +53,8 @@ namespace Frac = let cd = gcd(n, d) Pos(n / cd, d / cd) +/** Integer to rational division + */ function make_frac(n : int, d : int) : frac = if (d == 0) abort("Division by zero") elif (n == 0) Zero @@ -111,7 +117,9 @@ namespace Frac = Zero => 0 Neg(n, d) => -(n + d - 1) / d - // Round towards nearest integer. If two integers are in the same distance, choose the even one. +/** Round towards nearest integer. If two integers are in the same + * distance, choose the even one. + */ function round(f : frac) : int = let fl = floor(f) let cl = ceil(f) @@ -146,6 +154,8 @@ namespace Frac = function div(a : frac, b : frac) : frac = mul(a, inv(b)) +/** `b` to the power of `e` + */ function int_exp(b : frac, e : int) : frac = if (sign(b) == 0 && e == 0) abort("Zero to the zero exponentation") elif (e < 0) inv(int_exp_(b, -e)) @@ -158,8 +168,9 @@ namespace Frac = if (e mod 2 == 1) mul(mul(half, half), b) else mul(half, half) - // Reduces the fraction's in-memory size by dividing its components by two until the - // the error is bigger than `loss` value +/** Reduces the fraction's in-memory size by dividing its components by two until the + * the error is bigger than `loss` value + */ function optimize(f : frac, loss : frac) : frac = require(geq(loss, Zero), "negative loss optimize") let s = sign(f) diff --git a/priv/stdlib/Func.aes b/priv/stdlib/Func.aes index 7d633a2..42cef77 100644 --- a/priv/stdlib/Func.aes +++ b/priv/stdlib/Func.aes @@ -12,35 +12,66 @@ namespace Func = function rapply(x : 'a, f : 'a => 'b) : 'b = f(x) - /* The Z combinator - replacement for local and anonymous recursion. - */ +/** The Z combinator - replacement for local and anonymous recursion. + */ function recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res = (x) => f(recur(f), x) +/** n-times composition with itself + */ function iter(n : int, f : 'a => 'a) : 'a => 'a = iter_(n, f, (x) => x) private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a = if(n == 0) acc elif(n == 1) comp(f, acc) else iter_(n / 2, comp(f, f), if(n mod 2 == 0) acc else comp(f, acc)) - function curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c) = +/** Turns an ugly, bad and disgusting arity-n function into + * a beautiful and sweet function taking the first argument + * and returning a function watiting for the remaining ones + * in the same manner + */ + function curry2(f : ('a, 'b) => 'x) : 'a => ('b => 'x) = (x) => (y) => f(x, y) - function curry3(f : ('a, 'b, 'c) => 'd) : 'a => ('b => ('c => 'd)) = + function curry3(f : ('a, 'b, 'c) => 'x) : 'a => ('b => ('c => 'x)) = (x) => (y) => (z) => f(x, y, z) + function curry4(f : ('a, 'b, 'c, 'd) => 'x) : 'a => ('b => ('c => ('d => 'x))) = + (x) => (y) => (z) => (w) => f(x, y, z, w) + function curry5(f : ('a, 'b, 'c, 'd, 'e) => 'x) : 'a => ('b => ('c => ('d => ('e => 'x)))) = + (x) => (y) => (z) => (w) => (q) => f(x, y, z, w, q) - function uncurry2(f : 'a => ('b => 'c)) : ('a, 'b) => 'c = +/** Opposite of curry. Gross + */ + function uncurry2(f : 'a => ('b => 'x)) : ('a, 'b) => 'x = (x, y) => f(x)(y) - function uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd = + function uncurry3(f : 'a => ('b => ('c => 'x))) : ('a, 'b, 'c) => 'x = (x, y, z) => f(x)(y)(z) + function uncurry4(f : 'a => ('b => ('c => ('d => 'x)))) : ('a, 'b, 'c, 'd) => 'x = + (x, y, z, w) => f(x)(y)(z)(w) + function uncurry5(f : 'a => ('b => ('c => ('d => ('e => 'x))))) : ('a, 'b, 'c, 'd, 'e) => 'x = + (x, y, z, w, q) => f(x)(y)(z)(w)(q) - function tuplify2(f : ('a, 'b) => 'c) : (('a * 'b)) => 'c = +/** Turns an arity-n function into a function taking n-tuple + */ + function tuplify2(f : ('a, 'b) => 'x) : (('a * 'b)) => 'x = (t) => switch(t) (x, y) => f(x, y) - function tuplify3(f : ('a, 'b, 'c) => 'd) : 'a * 'b * 'c => 'd = + function tuplify3(f : ('a, 'b, 'c) => 'x) : 'a * 'b * 'c => 'x = (t) => switch(t) (x, y, z) => f(x, y, z) + function tuplify4(f : ('a, 'b, 'c, 'd) => 'x) : 'a * 'b * 'c * 'd => 'x = + (t) => switch(t) + (x, y, z, w) => f(x, y, z, w) + function tuplify5(f : ('a, 'b, 'c, 'd, 'e) => 'x) : 'a * 'b * 'c * 'd * 'e => 'x = + (t) => switch(t) + (x, y, z, w, q) => f(x, y, z, w, q) - function untuplify2(f : 'a * 'b => 'c) : ('a, 'b) => 'c = +/** Opposite of tuplify + */ + function untuplify2(f : 'a * 'b => 'x) : ('a, 'b) => 'x = (x, y) => f((x, y)) - function untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd = + function untuplify3(f : 'a * 'b * 'c => 'x) : ('a, 'b, 'c) => 'x = (x, y, z) => f((x, y, z)) + function untuplify4(f : 'a * 'b * 'c * 'd => 'x) : ('a, 'b, 'c, 'd) => 'x = + (x, y, z, w) => f((x, y, z, w)) + function untuplify5(f : 'a * 'b * 'c * 'd * 'e => 'x) : ('a, 'b, 'c, 'd, 'e) => 'x = + (x, y, z, w, q) => f((x, y, z, w, q)) diff --git a/priv/stdlib/List.aes b/priv/stdlib/List.aes index 64c4fd5..3405035 100644 --- a/priv/stdlib/List.aes +++ b/priv/stdlib/List.aes @@ -19,10 +19,15 @@ namespace List = [x] => Some(x) _::t => last(t) +/** Finds first element of `l` fulfilling predicate `p` as `Some` or `None` + * if no such element exists. + */ function find(p : 'a => bool, l : list('a)) : option('a) = switch(l) [] => None h::t => if(p(h)) Some(h) else find(p, t) +/** Returns list of all indices of elements from `l` that fulfill the predicate `p`. + */ function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, []) private function find_indices_( p : 'a => bool , l : list('a) @@ -50,14 +55,22 @@ namespace List = _::t => length_(t, acc + 1) +/** Creates an ascending sequence of all integer numbers + * between `a` and `b` (including `a` and `b`) + */ function from_to(a : int, b : int) : list(int) = [a..b] +/** Creates an ascending sequence of integer numbers betweeen + * `a` and `b` jumping by given `step`. Includes `a` and takes + * `b` only if `(b - a) mod step == 0`. `step` should be bigger than 0. + */ function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, []) private function from_to_step_(a, b, s, acc) = if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc) - /* Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow */ +/** Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow + */ function replace_at(n : int, e : 'a, l : list('a)) : list('a) = if(n<0) abort("insert_at underflow") else replace_at_(n, e, l, []) private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = @@ -66,7 +79,8 @@ namespace List = h::t => if (n == 0) reverse(e::acc) ++ t else replace_at_(n-1, e, t, h::acc) - /* Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow */ +/** Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow + */ function insert_at(n : int, e : 'a, l : list('a)) : list('a) = if(n<0) abort("insert_at underflow") else insert_at_(n, e, l, []) private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = @@ -75,6 +89,9 @@ namespace List = [] => abort("insert_at overflow") h::t => insert_at_(n-1, e, t, h::acc) +/** Assuming that cmp represents `<` comparison, inserts `x` before + * the first element in the list `l` which is greater than it + */ function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = insert_by_(cmp, x, l, []) private function insert_by_(cmp : (('a, 'a) => bool), x : 'a, l : list('a), acc : list('a)) : list('a) = @@ -109,6 +126,8 @@ namespace List = [] => reverse(acc) h::t => map_(f, t, f(h)::acc) +/** Effectively composition of `map` and `flatten` + */ function flat_map(f : 'a => list('b), l : list('a)) : list('b) = ListInternal.flat_map(f, l) @@ -117,7 +136,8 @@ namespace List = [] => reverse(acc) h::t => filter_(p, t, if(p(h)) h::acc else acc) - /* Take `n` first elements */ +/** Take `n` first elements + */ function take(n : int, l : list('a)) : list('a) = if(n < 0) abort("Take negative number of elements") else take_(n, l, []) private function take_(n : int, l : list('a), acc : list('a)) : list('a) = @@ -126,7 +146,8 @@ namespace List = [] => reverse(acc) h::t => take_(n-1, t, h::acc) - /* Drop `n` first elements */ +/** Drop `n` first elements + */ function drop(n : int, l : list('a)) : list('a) = if(n < 0) abort("Drop negative number of elements") elif (n == 0) l @@ -134,18 +155,23 @@ namespace List = [] => [] h::t => drop(n-1, t) - /* Get the longest prefix of a list in which every element matches predicate `p` */ +/** Get the longest prefix of a list in which every element + * matches predicate `p` + */ function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, []) private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) [] => reverse(acc) h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc) - /* Drop elements from `l` until `p` holds */ +/** Drop elements from `l` until `p` holds + */ function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l) [] => [] h::t => if(p(h)) drop_while(p, t) else l - /* Splits list into two lists of elements that respectively match and don't match predicate `p` */ +/** Splits list into two lists of elements that respectively + * match and don't match predicate `p` + */ function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], []) private function partition_( p : 'a => bool , l : list('a) @@ -155,7 +181,8 @@ namespace List = [] => (reverse(acc_t), reverse(acc_f)) h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f) - +/** Flattens list of lists into a single list + */ function flatten(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll) function all(p : 'a => bool, l : list('a)) : bool = switch(l) @@ -171,7 +198,9 @@ namespace List = function product(l : list(int)) : int = foldl((a, b) => a * b, 1, l) - /* Zips two list by applying bimapping function on respective elements. Drops longer tail. */ +/** Zips two list by applying bimapping function on respective elements. + * Drops longer tail. + */ function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, []) private function zip_with_( f : ('a, 'b) => 'c , l1 : list('a) @@ -181,7 +210,8 @@ namespace List = (h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc) _ => reverse(acc) - /* Zips two lists into list of pairs. Drops longer tail. */ +/** Zips two lists into list of pairs. Drops longer tail. + */ function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2) function unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], []) @@ -199,7 +229,8 @@ namespace List = h::t => switch (partition((x) => lesser_cmp(x, h), t)) (lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger) - +/** Puts `delim` between every two members of the list + */ function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, []) private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l) [] => reverse(acc) @@ -207,6 +238,8 @@ namespace List = h::t => intersperse_(delim, t, delim::h::acc) +/** Effectively a zip with an infinite sequence of natural numbers + */ function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0, []) private function enumerate_(l : list('a), n : int, acc : list(int * 'a)) : list(int * 'a) = switch(l) [] => reverse(acc) diff --git a/priv/stdlib/Option.aes b/priv/stdlib/Option.aes index 6ebf98c..485ace6 100644 --- a/priv/stdlib/Option.aes +++ b/priv/stdlib/Option.aes @@ -10,13 +10,18 @@ namespace Option = None => false Some(_) => true - +/** Catamorphism on `option`. Also known as inlined pattern matching. + */ function match(n : 'b, s : 'a => 'b, o : option('a)) : 'b = switch(o) None => n Some(x) => s(x) +/** Escape option providing default if `None` + */ function default(def : 'a, o : option('a)) : 'a = match(def, (x) => x, o) +/** Assume it is `Some` + */ function force(o : option('a)) : 'a = default(abort("Forced None value"), o) function on_elem(o : option('a), f : 'a => unit) : unit = match((), f, o) @@ -40,10 +45,14 @@ namespace Option = (Some(x1), Some(x2), Some(x3)) => Some(f(x1, x2, x3)) _ => None +/** Like `map`, but the function is in `option` + */ function app_over(f : option ('a => 'b), o : option('a)) : option('b) = switch((f, o)) (Some(ff), Some(xx)) => Some(ff(xx)) _ => None +/** Monadic bind + */ function flat_map(f : 'a => option('b), o : option('a)) : option('b) = switch(o) None => None Some(x) => f(x) @@ -53,22 +62,31 @@ namespace Option = None => [] Some(x) => [x] +/** Turns list of options into a list of elements that are under `Some`s. + * Safe. + */ function filter_options(l : list(option('a))) : list('a) = filter_options_(l, []) private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l) [] => List.reverse(acc) None::t => filter_options_(t, acc) Some(x)::t => filter_options_(t, x::acc) +/** Just like `filter_options` but requires all elements to be `Some` and returns + * None if any of them is not + */ function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, []) private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l) [] => Some(List.reverse(acc)) None::t => None Some(x)::t => seq_options_(t, x::acc) - +/** Choose `Some` out of two if possible + */ function choose(o1 : option('a), o2 : option('a)) : option('a) = if(is_some(o1)) o1 else o2 +/** Choose `Some` from list of options if possible + */ function choose_first(l : list(option('a))) : option('a) = switch(l) [] => None None::t => choose_first(t) diff --git a/priv/stdlib/Pair.aes b/priv/stdlib/Pair.aes index 22312e3..16ec30f 100644 --- a/priv/stdlib/Pair.aes +++ b/priv/stdlib/Pair.aes @@ -6,12 +6,18 @@ namespace Pair = function snd(t : ('a * 'b)) : 'b = switch(t) (_, y) => y +/** Map over first + */ function map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) = switch(t) (x, y) => (f(x), y) +/** Map over second + */ function map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) = switch(t) (x, y) => (x, f(y)) +/** Map over both + */ function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) = switch(t) (x, y) => (f(x), g(y)) diff --git a/priv/stdlib/Triple.aes b/priv/stdlib/Triple.aes index 84f3ddd..07a716d 100644 --- a/priv/stdlib/Triple.aes +++ b/priv/stdlib/Triple.aes @@ -10,15 +10,23 @@ namespace Triple = (_, _, z) => z +/** Map over first + */ function map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) = switch(t) (x, y, z) => (f(x), y, z) +/** Map over second + */ function map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) = switch(t) (x, y, z) => (x, f(y), z) +/** Map over third + */ function map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) = switch(t) (x, y, z) => (x, y, f(z)) +/** Map over all elements + */ function trimap( f : 'a => 'x , g : 'b => 'y , h : 'c => 'z @@ -29,9 +37,13 @@ namespace Triple = function swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) = switch(t) (x, y, z) => (z, y, x) +/** Right rotation + */ function rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) = switch(t) (x, y, z) => (z, x, y) +/** Left rotation + */ function rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) = switch(t) (x, y, z) => (y, z, x)