diff options
author | rubytree <rt@rubytree.me> | 2023-04-30 13:09:26 +0200 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2023-05-13 16:32:38 +0100 |
commit | 037e2c575c367666a952f99ea4d9cc42268cedec (patch) | |
tree | fe0857f1ac1593696bfbad333c14e1ee3881cc1c /src | |
parent | 37b4a44fe8f63b6314f3611f8ec3dab820f34e3a (diff) | |
download | gleam_stdlib-037e2c575c367666a952f99ea4d9cc42268cedec.tar.gz gleam_stdlib-037e2c575c367666a952f99ea4d9cc42268cedec.zip |
`dynamic.tupleN/2` optimization
Diffstat (limited to 'src')
-rw-r--r-- | src/gleam/dynamic.gleam | 158 | ||||
-rw-r--r-- | src/gleam_stdlib.erl | 31 | ||||
-rw-r--r-- | src/gleam_stdlib.mjs | 43 |
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) { |