Last active
October 22, 2023 23:25
-
-
Save tynanbe/0d2a870ad099c2196598277016aef85f to your computer and use it in GitHub Desktop.
Proposed Gleam tuple spread syntax, Erlang target proof of concept
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import gleam/dynamic.{DecodeErrors, Dynamic, field, int, list, string} | |
import gleam/io | |
import gleam/json | |
import gleam/list | |
import gleam/result | |
type DynResult(a) = | |
Result(a, DecodeErrors) | |
pub opaque type Collector(a) { | |
Collector(data: Dynamic, result: DynResult(a)) | |
} | |
pub fn collector(data: Dynamic) -> Collector(#()) { | |
Collector(data: data, result: Ok(#())) | |
} | |
// Spread tuple type's elements into another tuple type | |
pub fn get( | |
from prev: Collector(a), | |
with decoder: fn(Dynamic) -> DynResult(b), | |
) -> Collector(spread_a_and_append_b_into_tuple) { | |
let Collector(data, result) = prev | |
case decoder(data), result { | |
Ok(value), Ok(tuple) -> | |
// Spread tuple's elements into another tuple | |
tuple_spread_into_tuple(tuple, value) | |
|> Ok | |
Error(new_errors), Error(errors) -> | |
errors | |
|> list.append(new_errors) | |
|> Error | |
Error(errors), _ | _, Error(errors) -> Error(errors) | |
} | |
|> Collector(data: data) | |
} | |
pub fn apply( | |
from prev: Collector(a), | |
// Spread tuple type's elements into fn type's parameters | |
to constructor: spread_a_into_fn_params_returning_b, | |
) -> DynResult(b) { | |
use args <- result.map(over: prev.result) | |
// Spread tuple's elements into fn's arguments | |
tuple_spread_into_constructor(args, constructor) | |
} | |
@external(erlang, "erlang", "append_element") | |
fn tuple_spread_into_tuple(tuple: a, value: b) -> c | |
fn tuple_spread_into_constructor(tuple: a, constructor: b) -> c { | |
do_apply(constructor, tuple_to_list(tuple)) | |
} | |
@external(erlang, "erlang", "apply") | |
fn do_apply(constructor: f, args: dyn_list) -> record | |
@external(erlang, "erlang", "tuple_to_list") | |
fn tuple_to_list(tuple: a) -> dyn_list | |
/// Begin Demo | |
pub type FooBar { | |
FooBar(foo: Int, bar: String) | |
} | |
pub fn decode_foobar(data: Dynamic) -> DynResult(FooBar) { | |
collector(data) | |
|> get(field("foo", of: int)) | |
|> get(field("bar", of: string)) | |
|> apply(to: FooBar) | |
} | |
pub fn decode_foobars(data: String) -> Result(List(FooBar), json.DecodeError) { | |
let decoder = list(of: decode_foobar) | |
json.decode(from: data, using: decoder) | |
} | |
pub fn bad_decode_foobars( | |
data: String, | |
) -> Result(List(FooBar), json.DecodeError) { | |
let decode_foobar = fn(data) { | |
collector(data) | |
|> get(field("foo", of: string)) | |
|> get(field("bar", of: int)) | |
|> apply(to: FooBar) | |
} | |
let decoder = list(of: decode_foobar) | |
json.decode(from: data, using: decoder) | |
} | |
pub fn old_decode_foobars( | |
data: String, | |
) -> Result(List(FooBar), json.DecodeError) { | |
let decode_foobar = | |
FooBar | |
|> dynamic.decode2(field("foo", of: int), field("bar", of: string)) | |
let decoder = list(of: decode_foobar) | |
json.decode(from: data, using: decoder) | |
} | |
pub fn main() { | |
let data = | |
" | |
[ | |
{ | |
\"foo\" : 0, | |
\"bar\" : \"a\" | |
}, | |
{ | |
\"foo\" : 1, | |
\"bar\" : \"b\" | |
} | |
] | |
" | |
data | |
|> old_decode_foobars | |
|> io.debug | |
data | |
|> decode_foobars | |
|> io.debug | |
data | |
|> bad_decode_foobars | |
|> io.debug | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment