aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrubytree <rt@rubytree.me>2023-04-30 13:09:26 +0200
committerLouis Pilfold <louis@lpil.uk>2023-05-13 16:32:38 +0100
commit037e2c575c367666a952f99ea4d9cc42268cedec (patch)
treefe0857f1ac1593696bfbad333c14e1ee3881cc1c /src
parent37b4a44fe8f63b6314f3611f8ec3dab820f34e3a (diff)
downloadgleam_stdlib-037e2c575c367666a952f99ea4d9cc42268cedec.tar.gz
gleam_stdlib-037e2c575c367666a952f99ea4d9cc42268cedec.zip
`dynamic.tupleN/2` optimization
Diffstat (limited to 'src')
-rw-r--r--src/gleam/dynamic.gleam158
-rw-r--r--src/gleam_stdlib.erl31
-rw-r--r--src/gleam_stdlib.mjs43
3 files changed, 113 insertions, 119 deletions
diff --git a/src/gleam/dynamic.gleam b/src/gleam/dynamic.gleam
index 58536dd..0f1b754 100644
--- a/src/gleam/dynamic.gleam
+++ b/src/gleam/dynamic.gleam
@@ -141,6 +141,10 @@ if erlang {
}
})
}
+
+ fn put_expected(error: DecodeError, expected: String) -> DecodeError {
+ DecodeError(..error, expected: expected)
+ }
}
if javascript {
@@ -592,25 +596,12 @@ pub fn element(at index: Int, of inner_type: Decoder(t)) -> Decoder(t) {
}
}
-fn exact_decode_tuple_error(size: Int, data: Dynamic) -> Result(a, DecodeErrors) {
- let s = case size {
- 0 -> ""
- _ -> "s"
- }
- let error =
- ["Tuple of ", int.to_string(size), " element", s]
- |> string_builder.from_strings
- |> string_builder.to_string
- |> DecodeError(found: classify(data), path: [])
- Error([error])
-}
-
fn at_least_decode_tuple_error(
size: Int,
data: Dynamic,
) -> Result(a, DecodeErrors) {
let s = case size {
- s if s <= 1 -> ""
+ 1 -> ""
_ -> "s"
}
let error =
@@ -621,22 +612,6 @@ fn at_least_decode_tuple_error(
Error([error])
}
-fn exact_decode_list_error(size: Int, data: Dynamic) -> Result(a, DecodeErrors) {
- let expected = list_size_error_msg(size)
- let error = DecodeError(expected: expected, found: classify(data), path: [])
- Error([error])
-}
-
-fn list_size_error_msg(size: Int) -> String {
- let s = case size {
- s if s <= 1 -> ""
- _ -> "s"
- }
- ["List of ", int.to_string(size), " element", s]
- |> string_builder.from_strings
- |> string_builder.to_string
-}
-
// A tuple of unknown size
external type UnknownTuple
@@ -644,28 +619,52 @@ if erlang {
external fn decode_tuple(Dynamic) -> Result(UnknownTuple, DecodeErrors) =
"gleam_stdlib" "decode_tuple"
+ external fn decode_tuple2(Dynamic) -> Result(#(Dynamic, Dynamic), DecodeErrors) =
+ "gleam_stdlib" "decode_tuple2"
+
+ external fn decode_tuple3(Dynamic) -> Result(#(Dynamic, Dynamic, Dynamic), DecodeErrors) =
+ "gleam_stdlib" "decode_tuple3"
+
+ external fn decode_tuple4(Dynamic) -> Result(#(Dynamic, Dynamic, Dynamic, Dynamic), DecodeErrors) =
+ "gleam_stdlib" "decode_tuple4"
+
+ external fn decode_tuple5(Dynamic) -> Result(#(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic), DecodeErrors) =
+ "gleam_stdlib" "decode_tuple5"
+
+ external fn decode_tuple6(Dynamic) -> Result(#(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic, Dynamic), DecodeErrors) =
+ "gleam_stdlib" "decode_tuple6"
+
external fn tuple_get(UnknownTuple, Int) -> Result(Dynamic, DecodeErrors) =
"gleam_stdlib" "tuple_get"
external fn tuple_size(UnknownTuple) -> Int =
"gleam_stdlib" "size_of_tuple"
-
- external fn list_to_tuple(Dynamic) -> Result(Dynamic, DecodeErrors) =
- "gleam_stdlib" "list_to_tuple"
}
if javascript {
external fn decode_tuple(Dynamic) -> Result(UnknownTuple, DecodeErrors) =
"../gleam_stdlib.mjs" "decode_tuple"
+ external fn decode_tuple2(Dynamic) -> Result(#(Dynamic, Dynamic), DecodeErrors) =
+ "../gleam_stdlib.mjs" "decode_tuple2"
+
+ external fn decode_tuple3(Dynamic) -> Result(#(Dynamic, Dynamic, Dynamic), DecodeErrors) =
+ "../gleam_stdlib.mjs" "decode_tuple3"
+
+ external fn decode_tuple4(Dynamic) -> Result(#(Dynamic, Dynamic, Dynamic, Dynamic), DecodeErrors) =
+ "../gleam_stdlib.mjs" "decode_tuple4"
+
+ external fn decode_tuple5(Dynamic) -> Result(#(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic), DecodeErrors) =
+ "../gleam_stdlib.mjs" "decode_tuple5"
+
+ external fn decode_tuple6(Dynamic) -> Result(#(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic, Dynamic), DecodeErrors) =
+ "../gleam_stdlib.mjs" "decode_tuple6"
+
external fn tuple_get(UnknownTuple, Int) -> Result(Dynamic, DecodeErrors) =
"../gleam_stdlib.mjs" "tuple_get"
external fn tuple_size(UnknownTuple) -> Int =
"../gleam_stdlib.mjs" "length"
-
- external fn list_to_tuple(Dynamic) -> Result(Dynamic, DecodeErrors) =
- "../gleam_stdlib.mjs" "list_to_tuple"
}
fn tuple_errors(
@@ -678,64 +677,6 @@ fn tuple_errors(
}
}
-fn ensure_tuple(
- value: Dynamic,
- desired_size: Int,
-) -> Result(Dynamic, DecodeErrors) {
- case classify(value) {
- "Tuple" <> _ -> assert_is_tuple(value, desired_size)
- "List" -> {
- use _ <- result.then(assert_is_list(value, desired_size))
- list_to_tuple(value)
- }
- _ -> exact_decode_tuple_error(desired_size, value)
- }
-}
-
-fn assert_is_tuple(
- value: Dynamic,
- desired_size: Int,
-) -> Result(Dynamic, DecodeErrors) {
- let expected =
- string_builder.to_string(string_builder.from_strings([
- "Tuple of ",
- int.to_string(desired_size),
- " elements",
- ]))
- use tuple <- result.try(map_errors(
- decode_tuple(value),
- put_expected(_, expected),
- ))
- case tuple_size(tuple) {
- size if size == desired_size -> Ok(value)
- _ -> exact_decode_tuple_error(desired_size, value)
- }
-}
-
-fn assert_is_list(
- value: Dynamic,
- desired_size: Int,
-) -> Result(List(Dynamic), DecodeErrors) {
- let expected = list_size_error_msg(desired_size)
- use list <- result.then(map_errors(
- decode_list(value),
- put_expected(_, expected),
- ))
- case list.length(list) {
- size if size == desired_size -> Ok(list)
- size if size != desired_size -> {
- let found = list_size_error_msg(size)
- let error = DecodeError(expected: expected, found: found, path: [])
- Error([error])
- }
- _ -> exact_decode_list_error(desired_size, value)
- }
-}
-
-fn put_expected(error: DecodeError, expected: String) -> DecodeError {
- DecodeError(..error, expected: expected)
-}
-
fn push_path(error: DecodeError, name: t) -> DecodeError {
let name = from(name)
let decoder = any([string, fn(x) { result.map(int(x), int.to_string) }])
@@ -797,8 +738,7 @@ pub fn tuple2(
second decode2: Decoder(b),
) -> Decoder(#(a, b)) {
fn(value) {
- use tuple <- result.try(ensure_tuple(value, 2))
- let #(a, b) = unsafe_coerce(tuple)
+ use #(a, b) <- result.try(decode_tuple2(value))
case decode1(a), decode2(b) {
Ok(a), Ok(b) -> Ok(#(a, b))
a, b ->
@@ -834,7 +774,7 @@ pub fn tuple2(
///
/// ```gleam
/// > from([from(1), from(2.0), from("3")])
-/// > |> tuple2(int, float, string)
+/// > |> tuple3(int, float, string)
/// Ok(#(1, 2.0, "3"))
/// ```
///
@@ -860,8 +800,7 @@ pub fn tuple3(
third decode3: Decoder(c),
) -> Decoder(#(a, b, c)) {
fn(value) {
- use tuple <- result.try(ensure_tuple(value, 3))
- let #(a, b, c) = unsafe_coerce(tuple)
+ use #(a, b, c) <- result.try(decode_tuple3(value))
case decode1(a), decode2(b), decode3(c) {
Ok(a), Ok(b), Ok(c) -> Ok(#(a, b, c))
a, b, c ->
@@ -891,13 +830,13 @@ pub fn tuple3(
///
/// ```gleam
/// > from([1, 2, 3, 4])
-/// > |> tuple3(int, int, int, int)
+/// > |> tuple4(int, int, int, int)
/// Ok(#(1, 2, 3, 4))
/// ```
///
/// ```gleam
/// > from([from(1), from(2.0), from("3"), from(4)])
-/// > |> tuple2(int, float, string, int)
+/// > |> tuple4(int, float, string, int)
/// Ok(#(1, 2.0, "3", 4))
/// ```
///
@@ -923,8 +862,7 @@ pub fn tuple4(
fourth decode4: Decoder(d),
) -> Decoder(#(a, b, c, d)) {
fn(value) {
- use tuple <- result.try(ensure_tuple(value, 4))
- let #(a, b, c, d) = unsafe_coerce(tuple)
+ use #(a, b, c, d) <- result.try(decode_tuple4(value))
case decode1(a), decode2(b), decode3(c), decode4(d) {
Ok(a), Ok(b), Ok(c), Ok(d) -> Ok(#(a, b, c, d))
a, b, c, d ->
@@ -956,13 +894,13 @@ pub fn tuple4(
///
/// ```gleam
/// > from([1, 2, 3, 4, 5])
-/// > |> tuple3(int, int, int, int, int)
+/// > |> tuple5(int, int, int, int, int)
/// Ok(#(1, 2, 3, 4, 5))
/// ```
///
/// ```gleam
/// > from([from(1), from(2.0), from("3"), from(4), from(True)])
-/// > |> tuple2(int, float, string, int, bool)
+/// > |> tuple5(int, float, string, int, bool)
/// Ok(#(1, 2.0, "3", 4, True))
/// ```
///
@@ -986,8 +924,7 @@ pub fn tuple5(
fifth decode5: Decoder(e),
) -> Decoder(#(a, b, c, d, e)) {
fn(value) {
- use tuple <- result.try(ensure_tuple(value, 5))
- let #(a, b, c, d, e) = unsafe_coerce(tuple)
+ use #(a, b, c, d, e) <- result.try(decode_tuple5(value))
case decode1(a), decode2(b), decode3(c), decode4(d), decode5(e) {
Ok(a), Ok(b), Ok(c), Ok(d), Ok(e) -> Ok(#(a, b, c, d, e))
a, b, c, d, e ->
@@ -1020,13 +957,13 @@ pub fn tuple5(
///
/// ```gleam
/// > from([1, 2, 3, 4, 5, 6])
-/// > |> tuple3(int, int, int, int, int, int)
+/// > |> tuple6(int, int, int, int, int, int)
/// Ok(#(1, 2, 3, 4, 5, 6))
/// ```
///
/// ```gleam
/// > from([from(1), from(2.0), from("3"), from(4), from(True), from(False)])
-/// > |> tuple2(int, float, string, int, bool, bool)
+/// > |> tuple6(int, float, string, int, bool, bool)
/// Ok(#(1, 2.0, "3", 4, True, False))
/// ```
///
@@ -1053,8 +990,7 @@ pub fn tuple6(
sixth decode6: Decoder(f),
) -> Decoder(#(a, b, c, d, e, f)) {
fn(value) {
- use tuple <- result.try(ensure_tuple(value, 6))
- let #(a, b, c, d, e, f) = unsafe_coerce(tuple)
+ use #(a, b, c, d, e, f) <- result.try(decode_tuple6(value))
case
decode1(a),
decode2(b),
diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl
index 2ede96a..79ded3f 100644
--- a/src/gleam_stdlib.erl
+++ b/src/gleam_stdlib.erl
@@ -8,10 +8,11 @@
bit_string_int_to_u32/1, bit_string_int_from_u32/1, decode_result/1,
bit_string_slice/3, decode_bit_string/1, compile_regex/2, regex_scan/2,
percent_encode/1, percent_decode/1, regex_check/2, regex_split/2,
- base_decode64/1, parse_query/1, bit_string_concat/1, size_of_tuple/1,
- decode_tuple/1, tuple_get/2, classify_dynamic/1, print/1, println/1,
- print_error/1, println_error/1, inspect/1, float_to_string/1,
- int_from_base_string/2, list_to_tuple/1]).
+ base_decode64/1, parse_query/1, bit_string_concat/1, size_of_tuple/1,
+ decode_tuple/1, decode_tuple2/1, decode_tuple3/1, decode_tuple4/1,
+ decode_tuple5/1, decode_tuple6/1, tuple_get/2, classify_dynamic/1,
+ print/1, println/1, print_error/1, println_error/1, inspect/1,
+ float_to_string/1, int_from_base_string/2]).
%% Taken from OTP's uri_string module
-define(DEC2HEX(X),
@@ -96,6 +97,26 @@ tuple_get(Data, Index) -> {ok, element(Index + 1, Data)}.
decode_tuple(Data) when is_tuple(Data) -> {ok, Data};
decode_tuple(Data) -> decode_error_msg(<<"Tuple">>, Data).
+decode_tuple2({_,_} = A) -> {ok, A};
+decode_tuple2([A,B]) -> {ok, {A,B}};
+decode_tuple2(Data) -> decode_error_msg(<<"Tuple or List of 2 elements">>, Data).
+
+decode_tuple3({_,_,_} = A) -> {ok, A};
+decode_tuple3([A,B,C]) -> {ok, {A,B,C}};
+decode_tuple3(Data) -> decode_error_msg(<<"Tuple or List of 3 elements">>, Data).
+
+decode_tuple4({_,_,_,_} = A) -> {ok, A};
+decode_tuple4([A,B,C,D]) -> {ok, {A,B,C,D}};
+decode_tuple4(Data) -> decode_error_msg(<<"Tuple or List of 4 elements">>, Data).
+
+decode_tuple5({_,_,_,_,_} = A) -> {ok, A};
+decode_tuple5([A,B,C,D,E]) -> {ok, {A,B,C,D,E}};
+decode_tuple5(Data) -> decode_error_msg(<<"Tuple or List of 5 elements">>, Data).
+
+decode_tuple6({_,_,_,_,_,_} = A) -> {ok, A};
+decode_tuple6([A,B,C,D,E,F]) -> {ok, {A,B,C,D,E,F}};
+decode_tuple6(Data) -> decode_error_msg(<<"Tuple or List of 6 elements">>, Data).
+
decode_option(Term, F) ->
Decode = fun(Inner) ->
case F(Inner) of
@@ -419,5 +440,3 @@ inspect_maybe_utf8_string(Binary, Acc) ->
float_to_string(Float) when is_float(Float) ->
erlang:iolist_to_binary(io_lib_format:fwrite_g(Float)).
-list_to_tuple(Data) when is_list(Data) -> {ok, erlang:list_to_tuple(Data)};
-list_to_tuple(Data) -> decode_error_msg(<<"List">>, Data). \ No newline at end of file
diff --git a/src/gleam_stdlib.mjs b/src/gleam_stdlib.mjs
index 102158c..d9825e7 100644
--- a/src/gleam_stdlib.mjs
+++ b/src/gleam_stdlib.mjs
@@ -616,8 +616,47 @@ export function decode_tuple(data) {
return Array.isArray(data) ? new Ok(data) : decoder_error("Tuple", data);
}
-export function list_to_tuple(data) {
- return List.isList(data) ? new Ok([...data]) : decoder_error("List", data);
+export function decode_tuple2(data) {
+ return decode_tupleN(data, 2);
+}
+
+export function decode_tuple3(data) {
+ return decode_tupleN(data, 3);
+}
+
+export function decode_tuple4(data) {
+ return decode_tupleN(data, 4);
+}
+
+export function decode_tuple5(data) {
+ return decode_tupleN(data, 5);
+}
+
+export function decode_tuple6(data) {
+ return decode_tupleN(data, 6);
+}
+
+function decode_tupleN(data, n) {
+ let error_message = `Tuple or List of ${n} elements`;
+
+ if (!(List.isList(data) || Array.isArray(data))) {
+ return decoder_error(error_message, data);
+ }
+
+ let array = List.isList(data) ? [...data] : data;
+
+ let i = 0;
+ for (; i < n; i++) {
+ if (array[i] === undefined) {
+ return decoder_error(error_message, data);
+ }
+ }
+
+ if (array[i] === undefined) {
+ return new Ok(array);
+ } else {
+ return decoder_error(error_message, data);
+ }
}
export function tuple_get(data, index) {