diff options
author | Louis Pilfold <louis@lpil.uk> | 2019-03-17 15:35:19 +0000 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2019-03-17 15:35:19 +0000 |
commit | fbd570295cd40802127913d2b308916eefb14066 (patch) | |
tree | 5a018e14b69d7748459091c97afa9e1cd7972d1d /src | |
parent | 00ff9767dc61e698aac791b43704cfce1ab33600 (diff) | |
download | gleam_stdlib-fbd570295cd40802127913d2b308916eefb14066.tar.gz gleam_stdlib-fbd570295cd40802127913d2b308916eefb14066.zip |
Any decoders
Diffstat (limited to 'src')
-rw-r--r-- | src/any.gleam | 287 | ||||
-rw-r--r-- | src/gleam__stdlib.erl | 39 | ||||
-rw-r--r-- | src/result.gleam | 21 |
3 files changed, 333 insertions, 14 deletions
diff --git a/src/any.gleam b/src/any.gleam index 884a436..41abed3 100644 --- a/src/any.gleam +++ b/src/any.gleam @@ -1,3 +1,7 @@ +import list +import result +import expect + // `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. @@ -14,3 +18,286 @@ pub external fn from(a) -> Any = "gleam__stdlib" "identity"; // native Erlang APIs. It is to be used as a last measure only. // pub external fn unsafeCoerce(a) -> b = "gleam__stdlib" "identity"; + +pub external fn string(Any) -> Result(String, String) + = "gleam__stdlib" "decode_string" + +test string { + "" + |> from + |> string + |> expect:equal(_, Ok("")) + + "Hello" + |> from + |> string + |> expect:equal(_, Ok("Hello")) + + 1 + |> from + |> string + |> expect:equal(_, Error("Expected a String, got `1`")) + + [] + |> from + |> string + |> expect:equal(_, Error("Expected a String, got `[]`")) +} + +pub external fn int(Any) -> Result(Int, String) + = "gleam__stdlib" "decode_int" + +test int { + 1 + |> from + |> int + |> expect:equal(_, Ok(1)) + + 2 + |> from + |> int + |> expect:equal(_, Ok(2)) + + 1.0 + |> from + |> int + |> expect:equal(_, Error("Expected an Int, got `1.0`")) + + [] + |> from + |> int + |> expect:equal(_, Error("Expected an Int, got `[]`")) +} + +pub external fn float(Any) -> Result(Float, String) + = "gleam__stdlib" "decode_float" + +test float { + 1.0 + |> from + |> float + |> expect:equal(_, Ok(1.0)) + + 2.2 + |> from + |> float + |> expect:equal(_, Ok(2.2)) + + 1 + |> from + |> float + |> expect:equal(_, Error("Expected a Float, got `1`")) + + [] + |> from + |> float + |> expect:equal(_, Error("Expected a Float, got `[]`")) +} + +// pub external fn atom(Any) -> Result(Atom, String) +// = "gleam__stdlib" "decode_atom" + +//// test atom { +//// // TODO +//// // make an atom here +//// // |> from +//// // |> atom +//// // |> expect:equal(_, Ok("")) + +//// // TODO +//// // make an atom here +//// // |> from +//// // |> atom +//// // |> expect:equal(_, Ok("ok")) + +//// let _ = 1 +//// |> from +//// |> atom +//// |> expect:is_error + +//// [] +//// |> from +//// |> atom +//// |> expect:is_error +//// } + +pub external fn bool(Any) -> Result(Bool, String) + = "gleam__stdlib" "decode_bool" + +test bool { + True + |> from + |> bool + |> expect:equal(_, Ok(True)) + + False + |> from + |> bool + |> expect:equal(_, Ok(False)) + + 1 + |> from + |> bool + |> expect:equal(_, Error("Expected a Bool, got `1`")) + + [] + |> from + |> bool + |> expect:equal(_, Error("Expected a Bool, got `[]`")) +} + +pub external fn thunk(Any) -> Result(fn() -> Any, String) + = "gleam__stdlib" "thunk" + +//// test thunk { +//// let _ = fn() { 1 } +//// |> from +//// |> thunk +//// |> expect:is_ok + +//// let _ = fn(x) { x } +//// |> from +//// |> thunk +//// |> expect:is_error + +//// let _ = 1 +//// |> from +//// |> thunk +//// |> expect:is_error + +//// [] +//// |> from +//// |> thunk +//// |> expect:is_error +//// } + +// external fn list_any(Any) -> Result(List(Any), String) = "gleam__stdlib" "decode_list" + +// fn list_module() { +// list +// } + +// pub fn list(any, decode) { +// any +// |> list_any +// |> result:then(_, fn(x) { list_module():traverse(x, decode) }) +// } + +//// test list { +//// let _ = [] +//// |> from +//// |> list(string) +//// |> expect:equal(_, Ok([])) + +//// let _ = [] +//// |> from +//// |> list(atom) +//// |> expect:equal(_, Ok([])) + +//// let _ = [1, 2, 3] +//// |> from +//// |> list(int) +//// |> expect:equal(_, Ok([1, 2, 3])) + +//// let _ = [[1], [2], [3]] +//// |> from +//// |> list(list(int)) +//// |> expect:equal(_, Ok([1, 2, 3])) + +//// let _ = 1 +//// |> from +//// |> list(string) +//// |> expect:is_error + +//// let _ = 1.0 +//// |> from() +//// |> list(int) +//// |> expect:is_error + +//// let _ = [""] +//// |> from() +//// |> list(int) +//// |> expect:is_error + +//// [from(1), any:from("not an int")] +//// |> from +//// |> list(int) +//// |> expect:is_error +//// } + +pub external fn tuple(Any) -> Result({Any, Any}, String) + = "gleam__stdlib" "decode_tuple" + +test tuple { + {1, []} + |> from + |> tuple + |> expect:equal(_, Ok({from(1), from([])})) + + {"ok", "ok"} + |> from + |> tuple + |> expect:equal(_, Ok({from("ok"), from("ok")})) + + {1} + |> from + |> tuple + |> expect:is_error + + {1, 2, 3} + |> from + |> tuple + |> expect:is_error + +// {1, 2.0} +// |> from +// |> tuple +// |> result:map(_, fn(x) { +// let {a, b} = x +// a |> int |> result:map(_, fn(x) { {x, b} }) +// }) +// // |> result:then(_, fn(x) { +// // let {a, b} = x +// // b |> float |> result:map(_, fn(x) { {a, x} }) +// // }) +// // |> expect:equal(_, Ok({1, 2.0})) +} + +////// TODO: FIXME: This doesn't work anymore because atoms are no longer a thing. +////// "Decode a field from a map, extracting a specified field. +////// +////// Multiple fields can be extracted and stored in a new record like so: +////// +////// Ok(name) <- decode:field(raw_data, \"name\") |> result:then(_, decode:string) +////// Ok(size) <- decode:field(raw_data, \"size\") |> result:then(_, decode:int) +////// Ok({ name = name, size = size }) +////// +////pub external fn field(Any, a) -> Result(String, Any) +//// = "gleam__stdlib" "field" + +////test field { +//// let _ = {ok = 1} +//// |> from +//// |> field("ok") +//// |> expect:equal(from(1)) + +//// let _ = {earlier = 2, ok = 3} +//// |> from +//// |> field("ok") +//// |> expect:equal(from(3)) + +//// let _ = {} +//// |> from +//// |> field("ok") +//// |> expect:is_error + +//// let _ = 1 +//// |> from +//// |> field("ok") +//// |> expect:is_error + +//// [] +//// |> from +//// |> field("ok") +//// |> expect:is_error +////} diff --git a/src/gleam__stdlib.erl b/src/gleam__stdlib.erl index 24668a7..f377e3f 100644 --- a/src/gleam__stdlib.erl +++ b/src/gleam__stdlib.erl @@ -1,9 +1,12 @@ -module(gleam__stdlib). -include_lib("eunit/include/eunit.hrl"). --export([expect_equal/2, expect_not_equal/2, expect_true/1, expect_false/1, expect_is_ok/1, - expect_is_error/1, atom_from_string/1, atom_create_from_string/1, atom_to_string/1, - map_fetch/2, iodata_append/2, iodata_prepend/2, identity/1]). +-export([expect_equal/2, expect_not_equal/2, expect_true/1, expect_false/1, + expect_is_ok/1, expect_is_error/1, atom_from_string/1, + atom_create_from_string/1, atom_to_string/1, map_fetch/2, + iodata_append/2, iodata_prepend/2, identity/1, decode_int/1, + decode_string/1, decode_bool/1, decode_float/1, decode_thunk/1, + decode_tuple/1, decode_field/2]). expect_equal(A, Expected) -> ?assertEqual(Expected, A). expect_not_equal(A, Expected) -> ?assertNotEqual(Expected, A). @@ -33,3 +36,33 @@ iodata_append(Iodata, String) -> [Iodata, String]. iodata_prepend(Iodata, String) -> [String, Iodata]. identity(X) -> X. + +decode_error_msg(Type, Data) -> + {error, iolist_to_binary(io_lib:format("Expected ~s, got `~p`", [Type, Data]))}. + +decode_string(Data) when is_binary(Data) -> {ok, Data}; +decode_string(Data) -> decode_error_msg("a String", Data). + +decode_int(Data) when is_integer(Data) -> {ok, Data}; +decode_int(Data) -> decode_error_msg("an Int", Data). + +decode_float(Data) when is_float(Data) -> {ok, Data}; +decode_float(Data) -> decode_error_msg("a Float", Data). + +decode_bool(Data) when is_boolean(Data) -> {ok, Data}; +decode_bool(Data) -> decode_error_msg("a Bool", Data). + +decode_thunk(Data) when is_function(Data, 0) -> {ok, Data}; +decode_thunk(Data) -> decode_error_msg("a zero arity function", Data). + +decode_tuple(Data = {_, _}) -> {ok, Data}; +decode_tuple(Data) -> decode_error_msg("a 2 element tuple", Data). + +decode_field(Data, Key) -> + case Data of + #{Key := Value} -> + {ok, Value}; + + _ -> + decode_error_msg(io_lib:format("a map with key `~p`", [Key]), Data) + end. diff --git a/src/result.gleam b/src/result.gleam index 113925e..18503ec 100644 --- a/src/result.gleam +++ b/src/result.gleam @@ -2,11 +2,6 @@ import expect // Result represents the result of something that may succeed or fail. // `Ok` means it was successful, `Error` means it failed. -// -pub enum Result(error, value) = - | Ok(value) - | Error(error) -; pub fn is_ok(result) { case result { @@ -38,7 +33,7 @@ test is_error { pub fn map(result, fun) { case result { | Ok(x) -> Ok(fun(x)) - | Error(_) -> result + | Error(e) -> Error(e) } } @@ -47,6 +42,10 @@ test map { |> map(_, fn(x) { x + 1 }) |> expect:equal(_, Ok(2)) + Ok(1) + |> map(_, fn(_) { "2" }) + |> expect:equal(_, Ok("2")) + Error(1) |> map(_, fn(x) { x + 1 }) |> expect:equal(_, Error(1)) @@ -90,7 +89,7 @@ test flatten { |> expect:equal(_, Error(Error(1))) } -pub fn flat_map(result, fun) { +pub fn then(result, fun) { case result { | Ok(x) -> case fun(x) { @@ -101,17 +100,17 @@ pub fn flat_map(result, fun) { } } -test flat_map { +test then { Error(1) - |> flat_map(_, fn(x) { Ok(x + 1) }) + |> then(_, fn(x) { Ok(x + 1) }) |> expect:equal(_, Error(1)) Ok(1) - |> flat_map(_, fn(x) { Ok(x + 1) }) + |> then(_, fn(x) { Ok(x + 1) }) |> expect:equal(_, Ok(2)) Ok(1) - |> flat_map(_, fn(_) { Error(1) }) + |> then(_, fn(_) { Error(1) }) |> expect:equal(_, Error(1)) } |