diff options
Diffstat (limited to 'src/std')
-rw-r--r-- | src/std/any.gleam | 57 | ||||
-rw-r--r-- | src/std/atom.gleam | 17 | ||||
-rw-r--r-- | src/std/bool.gleam | 38 | ||||
-rw-r--r-- | src/std/expect.gleam | 20 | ||||
-rw-r--r-- | src/std/float.gleam | 21 | ||||
-rw-r--r-- | src/std/http.gleam | 13 | ||||
-rw-r--r-- | src/std/int.gleam | 21 | ||||
-rw-r--r-- | src/std/iodata.gleam | 58 | ||||
-rw-r--r-- | src/std/list.gleam | 306 | ||||
-rw-r--r-- | src/std/map_dict.gleam | 100 | ||||
-rw-r--r-- | src/std/order.gleam | 48 | ||||
-rw-r--r-- | src/std/result.gleam | 51 | ||||
-rw-r--r-- | src/std/string.gleam | 36 | ||||
-rw-r--r-- | src/std/tuple.gleam | 29 |
14 files changed, 815 insertions, 0 deletions
diff --git a/src/std/any.gleam b/src/std/any.gleam new file mode 100644 index 0000000..c5c4a62 --- /dev/null +++ b/src/std/any.gleam @@ -0,0 +1,57 @@ +import std/list +import std/atom +import std/result + +fn list_module() { + list +} + +// `Any` data is data that we don"t know the type of yet. +// We likely get data like this from interop with Erlang, or from +// IO with the outside world. +// +pub external type Any; + +// Convert any Gleam data into `Any` data. +// +pub external fn from(a) -> Any = "gleam__stdlib" "identity"; + +// Unsafely cast any type into any other type. +// +// This is an escape hatch for the type system that may be useful when wrapping +// native Erlang APIs. It is to be used as a last measure only. +// +pub external fn unsafe_coerce(a) -> b = "gleam__stdlib" "identity"; + +pub external fn string(Any) -> Result(String, String) + = "gleam__stdlib" "decode_string" + +pub external fn int(Any) -> Result(Int, String) + = "gleam__stdlib" "decode_int" + +pub external fn float(Any) -> Result(Float, String) + = "gleam__stdlib" "decode_float" + +pub external fn atom(Any) -> Result(atom:Atom, String) + = "gleam__stdlib" "decode_atom" + +pub external fn bool(Any) -> Result(Bool, String) + = "gleam__stdlib" "decode_bool" + +pub external fn thunk(Any) -> Result(fn() -> Any, String) + = "gleam__stdlib" "decode_thunk" + +external fn list_any(Any) -> Result(List(Any), String) = + "gleam__stdlib" "decode_list" + +pub fn list(any, decode) { + any + |> list_any + |> result:then(_, list_module():traverse(_, decode)) +} + +pub external fn tuple(Any) -> Result({Any, Any}, String) + = "gleam__stdlib" "decode_tuple" + +pub external fn field(Any, a) -> Result(Any, String) + = "gleam__stdlib" "decode_field" diff --git a/src/std/atom.gleam b/src/std/atom.gleam new file mode 100644 index 0000000..67d040d --- /dev/null +++ b/src/std/atom.gleam @@ -0,0 +1,17 @@ +pub external type Atom; + +pub enum AtomNotLoaded = + | AtomNotLoaded + +pub external fn from_string(String) -> Result(Atom, AtomNotLoaded) = + "gleam__stdlib" "atom_from_string"; + +// This function can create a new atom if one does not already exist for +// the given string. Atoms are not garbage collected so this can result +// in a memory leak if called over time on new values +// +pub external fn create_from_string(String) -> Atom = + "gleam__stdlib" "atom_create_from_string"; + +pub external fn to_string(Atom) -> String = + "gleam__stdlib" "atom_to_string"; diff --git a/src/std/bool.gleam b/src/std/bool.gleam new file mode 100644 index 0000000..985f0d6 --- /dev/null +++ b/src/std/bool.gleam @@ -0,0 +1,38 @@ +import std/order + +pub fn negate(bool) { + case bool { + | True -> False + | False -> True + } +} + +pub fn compare(a, b) { + case {a, b} { + | {True, True} -> order:Eq + | {True, False} -> order:Gt + | {False, False} -> order:Eq + | {False, True} -> order:Lt + } +} + +pub fn max(a, b) { + case a { + | True -> True + | False -> b + } +} + +pub fn min(a, b) { + case a { + | False -> False + | True -> b + } +} + +pub fn to_int(bool) { + case bool { + | False -> 0 + | True -> 1 + } +} diff --git a/src/std/expect.gleam b/src/std/expect.gleam new file mode 100644 index 0000000..5ea6a93 --- /dev/null +++ b/src/std/expect.gleam @@ -0,0 +1,20 @@ +// TODO: Move this module into another package so it can be used as a +// dep only in test. + +pub external type Expectation; + +pub external fn equal(a, a) -> Expectation = "gleam__stdlib" "expect_equal"; + +pub external fn not_equal(a, a) -> Expectation = "gleam__stdlib" "expect_not_equal"; + +pub external fn true(Bool) -> Expectation = "gleam__stdlib" "expect_true"; + +pub external fn false(Bool) -> Expectation = "gleam__stdlib" "expect_false"; + +pub external fn is_ok(Result(a, b)) -> Expectation = "gleam__stdlib" "expect_is_ok"; + +pub external fn is_error(Result(a, b)) -> Expectation = "gleam__stdlib" "expect_is_error"; + +pub fn fail() { + true(False) +} diff --git a/src/std/float.gleam b/src/std/float.gleam new file mode 100644 index 0000000..8e1904e --- /dev/null +++ b/src/std/float.gleam @@ -0,0 +1,21 @@ +import std/iodata + +pub enum NotAFloat = + | NotAFloat + +pub external fn parse(String) -> Result(Float, NotAFloat) = + "gleam__stdlib" "parse_float"; + +pub fn to_string(f) { + f + |> iodata:from_float + |> iodata:to_string +} + +pub external fn ceiling(Float) -> Float = "math" "ceil"; + +pub external fn floor(Float) -> Float = "math" "floor"; + +pub external fn round(Float) -> Int = "erlang" "round"; + +pub external fn truncate(Float) -> Int = "erlang" "trunc"; diff --git a/src/std/http.gleam b/src/std/http.gleam new file mode 100644 index 0000000..d9942a8 --- /dev/null +++ b/src/std/http.gleam @@ -0,0 +1,13 @@ +// HTTP standard method as defined by RFC 2616, and PATCH which is defined by +// RFC 5789. +// +pub enum Method = + | Get + | Post + | Head + | Put + | Delete + | Trace + | Connect + | Options + | Patch diff --git a/src/std/int.gleam b/src/std/int.gleam new file mode 100644 index 0000000..b1f8256 --- /dev/null +++ b/src/std/int.gleam @@ -0,0 +1,21 @@ +import std/order + +pub enum NotAnInt = + | NotAnInt + +pub external fn parse(String) -> Result(Int, NotAnInt) = "gleam__stdlib" "parse_int"; + +pub external fn to_string(Int) -> String = "erlang" "integer_to_binary" + +pub external fn to_base_string(Int, Int) -> String = "erlang" "integer_to_binary" + +pub fn compare(a, b) { + case a == b { + | True -> order:Eq + | False -> + case a < b { + | True -> order:Lt + | False -> order:Gt + } + } +} diff --git a/src/std/iodata.gleam b/src/std/iodata.gleam new file mode 100644 index 0000000..56efc65 --- /dev/null +++ b/src/std/iodata.gleam @@ -0,0 +1,58 @@ +pub external type Iodata; + +pub external fn prepend(Iodata, String) -> Iodata = + "gleam__stdlib" "iodata_prepend"; + +pub external fn append(Iodata, String) -> Iodata = + "gleam__stdlib" "iodata_append"; + +pub external fn prepend_iodata(Iodata, Iodata) -> Iodata = + "gleam__stdlib" "iodata_prepend"; + +pub external fn append_iodata(Iodata, Iodata) -> Iodata = + "gleam__stdlib" "iodata_append"; + +pub external fn from_strings(List(String)) -> Iodata = + "gleam__stdlib" "identity"; + +pub external fn concat(List(Iodata)) -> Iodata = + "gleam__stdlib" "identity"; + +pub external fn new(String) -> Iodata = + "gleam__stdlib" "identity"; + +pub external fn to_string(Iodata) -> String = + "erlang" "iolist_to_binary"; + +pub external fn byte_size(Iodata) -> Int = + "erlang" "iolist_size"; + +pub external fn from_float(Float) -> Iodata = + "io_lib_format" "fwrite_g"; + +pub external fn lowercase(Iodata) -> Iodata = "string" "lowercase" + +pub external fn uppercase(Iodata) -> Iodata = "string" "uppercase" + +pub external fn reverse(Iodata) -> Iodata = "string" "reverse" + +enum Direction = + | All + +external fn erl_split(Iodata, String, Direction) -> List(Iodata) = + "string" "split" + +pub fn split(iodata, on) { + erl_split(iodata, on, All) +} + +external fn erl_replace(Iodata, String, String, Direction) -> Iodata = + "string" "replace" + +pub fn replace(iodata, pattern, replacement) { + erl_replace(iodata, pattern, replacement, All) +} + +pub external fn is_equal(Iodata, Iodata) -> Bool = "string" "equal" + +pub external fn is_empty(Iodata) -> Bool = "string" "is_empty" diff --git a/src/std/list.gleam b/src/std/list.gleam new file mode 100644 index 0000000..68dcb4a --- /dev/null +++ b/src/std/list.gleam @@ -0,0 +1,306 @@ +import std/int +import std/order + +pub enum Empty = + | Empty + +pub enum NotFound = + | NotFound + +pub enum LengthMismatch = + | LengthMismatch + +// Using the Erlang C BIF implementation. +// +pub external fn length(List(a)) -> Int = "erlang" "length" + +// Using the Erlang C BIF implementation. +// +pub external fn reverse(List(a)) -> List(a) = "lists" "reverse" + +pub fn is_empty(list) { + list == [] +} + +pub fn contains(list, elem) { + case list { + | [] -> False + | [head | rest] -> head == elem || contains(rest, elem) + } +} + +pub fn head(list) { + case list { + | [] -> Error(Empty) + | [x | _] -> Ok(x) + } +} + +pub fn tail(list) { + case list { + | [] -> Error(Empty) + | [_ | xs] -> Ok(xs) + } +} + +fn do_filter(list, fun, acc) { + case list { + | [] -> reverse(acc) + | [x | xs] -> + let new_acc = + case fun(x) { + | True -> [x | acc] + | False -> acc + } + do_filter(xs, fun, new_acc) + } +} + +pub fn filter(list, fun) { + do_filter(list, fun, []) +} + +fn do_map(list, fun, acc) { + case list { + | [] -> reverse(acc) + | [x | xs] -> do_map(xs, fun, [fun(x) | acc]) + } +} + +pub fn map(list, fun) { + do_map(list, fun, []) +} + +fn do_index_map(list, fun, index, acc) { + case list { + | [] -> reverse(acc) + | [x | xs] -> do_index_map(xs, fun, index + 1, [fun(index, x) | acc]) + } +} + +pub fn index_map(list, fun) { + do_index_map(list, fun, 0, []) +} + +fn do_traverse(list, fun, acc) { + case list { + | [] -> Ok(reverse(acc)) + | [x | xs] -> + case fun(x) { + | Ok(y) -> do_traverse(xs, fun, [y | acc]) + | Error(error) -> Error(error) + } + } +} + +pub fn traverse(list, fun) { + do_traverse(list, fun, []) +} + +pub fn drop(list, n) { + case n <= 0 { + | True -> list + | False -> + case list { + | [] -> [] + | [_ | xs] -> drop(xs, n - 1) + } + } +} + +fn do_take(list, n, acc) { + case n <= 0 { + | True -> reverse(acc) + | False -> + case list { + | [] -> reverse(acc) + | [x | xs] -> do_take(xs, n - 1, [x | acc]) + } + } +} + +pub fn take(list, n) { + do_take(list, n, []) +} + +pub fn new() { + [] +} + +pub external fn append(List(a), List(a)) -> List(a) = "lists" "append"; + +fn do_flatten(lists, acc) { + case lists { + | [] -> acc + | [l | rest] -> do_flatten(rest, append(acc, l)) + } +} + +pub fn flatten(lists) { + do_flatten(lists, []) +} + +pub fn fold(list, acc, fun) { + case list { + | [] -> acc + | [x | rest] -> fold(rest, fun(x, acc), fun) + } +} + +pub fn fold_right(list, acc, fun) { + case list { + | [] -> acc + | [x | rest] -> fun(x, fold_right(rest, acc, fun)) + } +} + +pub fn find(haystack, f) { + case haystack { + | [] -> Error(NotFound) + | [x | rest] -> + case f(x) { + | Ok(x) -> Ok(x) + | _ -> find(rest, f) + } + } +} + +pub fn all(list, f) { + case list { + | [] -> True + | [x | rest] -> + case f(x) { + | True -> all(rest, f) + | _ -> False + } + } +} + +pub fn any(list, f) { + case list { + | [] -> False + | [ x | rest] -> + case f(x) { + | False -> any(rest, f) + | _ -> True + } + } +} + +pub fn zip(l1, l2) { + case {l1, l2} { + | {[], _} -> [] + | {_, []} -> [] + | {[x1 | rest1], [x2 | rest2] } -> [ {x1, x2} | zip(rest1, rest2) ] + } +} + +pub fn strict_zip(l1, l2) { + case length(l1) == length(l2) { + | True -> Ok(zip(l1, l2)) + | False -> Error(LengthMismatch) + } +} + +pub fn intersperse(list, elem) { + case list { + | [] -> [] + | [x | []] -> [x] + | [x | rest] -> [x | [elem | intersperse(rest, elem)]] + } +} + +pub fn at(list, i) { + case i < 0 { + | True -> Error(NotFound) + | False -> + case list { + | [] -> Error(NotFound) + | [x | rest] -> + case i == 0 { + | True -> Ok(x) + | False -> at(rest, i - 1) + } + } + } +} + +pub fn unique(list) { + case list { + | [] -> [] + | [x | rest] -> [x | unique(filter(rest, fn(y) { y != x }))] + } +} + +fn merge_sort(a, b) { + case {a, b} { + | {[], _} -> b + | {_, []} -> a + | {[ax | ar], [bx | br]} -> + case ax < bx { + | True -> [ax | merge_sort(ar, b)] + | False -> [bx | merge_sort(a, br)] + } + } +} + +pub fn sort(list) { + let list_length = length(list) + case list_length < 2 { + | True -> list + | False -> + let split_length = list_length / 2 + let a_list = take(list, split_length) + let b_list = drop(list, split_length) + merge_sort(sort(a_list), sort(b_list)) + } +} + +pub fn range(start, stop) { + case int:compare(start, stop) { + | order:Eq -> [] + | order:Gt -> [start | range(start - 1, stop)] + | order:Lt -> [start | range(start + 1, stop)] + } +} + +fn do_repeat(a, times, acc) { + case times <= 0 { + | True -> acc + | False -> do_repeat(a, times - 1, [a | acc]) + } +} + +pub fn repeat(a, times) { + do_repeat(a, times, []) +} + +fn do_split(list, n, taken) { + case n <= 0 { + | True -> {reverse(taken), list} + | False -> + case list { + | [] -> {reverse(taken), []} + | [x | xs] -> do_split(xs, n - 1, [x | taken]) + } + } +} + +pub fn split(list, n) { + do_split(list, n, []) +} + +fn do_split_while(list, f, acc) { + case list { + | [] -> {reverse(acc), []} + | [x | xs] -> + case f(x) { + | False -> {reverse(acc), list} + | _ -> do_split_while(xs, f, [x | acc]) + } + } +} + +pub fn split_while(list, f) { + do_split_while(list, f, []) +} diff --git a/src/std/map_dict.gleam b/src/std/map_dict.gleam new file mode 100644 index 0000000..32065f5 --- /dev/null +++ b/src/std/map_dict.gleam @@ -0,0 +1,100 @@ +import std/any +import std/result +import std/list + +pub external type MapDict(key, value); + +pub enum NotFound = + | NotFound + +pub external fn size(MapDict(k, v)) -> Int + = "maps" "size" + +pub external fn to_list(MapDict(key, value)) -> List({key, value}) + = "maps" "to_list" + +pub external fn from_list(List({key, value})) -> MapDict(key, value) + = "maps" "from_list" + +external fn is_key(key, MapDict(key, v)) -> Bool + = "maps" "is_key" + +pub fn has_key(map, key) { + is_key(key, map) +} + +pub external fn new() -> MapDict(key, value) + = "maps" "new" + +pub external fn fetch(MapDict(key, value), key) -> Result(value, NotFound) + = "gleam__stdlib" "map_fetch"; + +external fn erl_put(key, value, MapDict(key, value)) -> MapDict(key, value) + = "maps" "put"; + +pub fn put(map, key, value) { + erl_put(key, value, map) +} + +external fn erl_map_values(fn(key, value) -> value, MapDict(key, value)) + -> MapDict(key, value) + = "maps" "map"; + +pub fn map_values(map, fun) { + erl_map_values(fun, map) +} + +pub external fn keys(MapDict(keys, v)) -> List(keys) + = "maps" "keys" + +pub external fn values(MapDict(k, values)) -> List(values) + = "maps" "values" + +external fn erl_filter(fn(key, value) -> Bool, MapDict(key, value)) + -> MapDict(key, value) + = "maps" "filter"; + +pub fn filter(map, fun) { + erl_filter(fun, map) +} + +external fn erl_take(List(k), MapDict(k, v)) -> MapDict(k, v) = "maps" "with" + +pub fn take(map, keys) { + erl_take(keys, map) +} + +pub external fn merge(MapDict(k, v), MapDict(k, v)) -> MapDict(k, v) = "maps" "merge" + +external fn erl_delete(k, MapDict(k, v)) -> MapDict(k, v) = "maps" "remove" + +pub fn delete(map, key) { + erl_delete(key, map) +} + +pub fn drop(map, keys) { + list:fold(keys, map, fn(key, acc) { + delete(acc, key) + }) +} + +pub external type NotFound; + +pub fn update(dict, key, f) { + case fetch(dict, key) { + | Ok(value) -> put(dict, key, f(Ok(value))) + | Error(_) -> put(dict, key, f(Error(NotFound))) + } +} + +fn do_fold(list, acc, f) { + case list { + | [] -> acc + | [{k, v} | tail] -> do_fold(tail, f(k, v, acc), f) + } +} + +pub fn fold(dict, acc, f) { + let kvs = to_list(dict) + do_fold(kvs, acc, f) +} diff --git a/src/std/order.gleam b/src/std/order.gleam new file mode 100644 index 0000000..4d39705 --- /dev/null +++ b/src/std/order.gleam @@ -0,0 +1,48 @@ +pub enum Order = + | Lt + | Eq + | Gt +; + +pub fn reverse(order) { + case order { + | Lt -> Gt + | Eq -> Eq + | Gt -> Lt + } +} + +pub fn to_int(order) { + case order { + | Lt -> -1 + | Eq -> 0 + | Gt -> 1 + } +} + +pub fn compare(a, b) { + case {a, b} { + | {Lt, Lt} -> Eq + | {Lt, _} -> Lt + | {Eq, Eq} -> Eq + | {Gt, Gt} -> Eq + | {Eq, Gt} -> Lt + | _ -> Gt + } +} + +pub fn max(a, b) { + case {a, b} { + | {Gt, _} -> Gt + | {Eq, Lt} -> Eq + | _ -> b + } +} + +pub fn min(a, b) { + case {a, b} { + | {Lt, _} -> Lt + | {Eq, Gt} -> Eq + | _ -> b + } +} diff --git a/src/std/result.gleam b/src/std/result.gleam new file mode 100644 index 0000000..133d9d5 --- /dev/null +++ b/src/std/result.gleam @@ -0,0 +1,51 @@ +// Result represents the result of something that may succeed or fail. +// `Ok` means it was successful, `Error` means it failed. + +pub fn is_ok(result) { + case result { + | Error(_) -> False + | Ok(_) -> True + } +} + +pub fn is_error(result) { + case result { + | Ok(_) -> False + | Error(_) -> True + } +} + +pub fn map(result, fun) { + case result { + | Ok(x) -> Ok(fun(x)) + | Error(e) -> Error(e) + } +} + +pub fn map_error(result, fun) { + case result { + | Ok(_) -> result + | Error(error) -> Error(fun(error)) + } +} + +pub fn flatten(result) { + case result { + | Ok(x) -> x + | Error(error) -> Error(error) + } +} + +pub fn then(result, fun) { + case result { + | Ok(x) -> fun(x) + | Error(e) -> Error(e) + } +} + +pub fn unwrap(result, default) { + case result { + | Ok(v) -> v + | Error(_) -> default + } +} diff --git a/src/std/string.gleam b/src/std/string.gleam new file mode 100644 index 0000000..9eacb37 --- /dev/null +++ b/src/std/string.gleam @@ -0,0 +1,36 @@ +import std/iodata +import std/list + +pub external fn length(String) -> Int = "string" "length" + +pub enum ParseError = + | ParseError + +pub external fn lowercase(String) -> String = "string" "lowercase" + +pub external fn uppercase(String) -> String = "string" "uppercase" + +pub fn reverse(string) { + string + |> iodata:new + |> iodata:reverse + |> iodata:to_string +} + +pub fn split(string, on) { + string + |> iodata:new + |> iodata:split(_, on) + |> list:map(_, iodata:to_string) +} + +pub fn replace(string, pattern, with) { + string + |> iodata:new + |> iodata:replace(_, pattern, with) + |> iodata:to_string +} + +pub fn append(s1, s2) { + iodata:new(s1) |> iodata:append(_, s2) |> iodata:to_string(_) +} diff --git a/src/std/tuple.gleam b/src/std/tuple.gleam new file mode 100644 index 0000000..ab38a74 --- /dev/null +++ b/src/std/tuple.gleam @@ -0,0 +1,29 @@ +import std/list + +pub fn new(a, b) { + {a, b} +} + +pub fn first(tup) { + let {a, _} = tup + a +} + +pub fn second(tup) { + let {_, a} = tup + a +} + +pub fn swap(tup) { + let {a, b} = tup + {b, a} +} + +pub fn fetch(haystack, needle) { + list:find(haystack, fn(tuple) { + case first(tuple) == needle { + | True -> tuple |> second |> Ok + | False -> Error([]) + } + }) +} |