How stable is the atom representation of gleam types, alternativly atom litterals? #3416
-
tldr: is it a safe assumption that this Ok(SomeValue) will be represented as this in Erlang? {ok, SomeValue} I've been playing around a bit with ffi in gleam, and I feel it's a bit cumbersome when dealing with return types, especially when it comes to tuples and atoms. From what I can tell gleam seems to handle -spec is_one(MaybeOne) -> {ok, binary()} | {error, binary()} when MaybeOne :: integer().
is_one(MaybeOne) ->
case MaybeOne of
1 -> {ok, <<"it is one"/utf8>>};
_ -> {error, <<"it is not one"/utf8>>}
end. @external(erlang, "my_module", "is_one")
fn is_one(maybe_one: Int) -> Result(String, String) But it feels quite hacky, because as far as I know this is internal implementation details and not part of "language contract", which means I can't rely on it still working in future versions. But on the other hand doing this the 'right way' also feels quite cumbersome especially since we can't really pattern match on atoms which are more or less ubiquitous in Erlang-Land: // lets pretend the inner types has some stuff in them
pub type InnerFooType {
InnerFooType
}
pub type InnerBarType {
InnerBarType
}
pub type MyNestedType {
NestedFooType(foo_type: InnerFooType)
NestedBarType(bar_type: InnerBarType)
}
pub fn my_pub_gleam_function(a: a, b: b) {
let dynamic_return_value = my_priv_gleam_function(a, b)
use partial_decoded <- result.try(dynamic.tuple2(
atom.from_dynamic,
dynamic.dynamic,
)(dynamic_return_value))
let foo_atom = get_atom("foo")
let bar_atom = get_atom("bar")
case partial_decoded {
#(atm, dyn_value) if atm == foo_atom ->
decode_inner_foo(dyn_value)
|> result.map(fn(inner_foo) { Ok(NestedFooType(inner_foo)) })
#(atm, dyn_value) if atm == bar_atom ->
decode_inner_bar(dyn_value)
|> result.map(fn(inner_bar) { Ok(NestedBarType(inner_bar)) })
_ -> Error([dynamic.DecodeError("unknown atom", "foo | bar", [])])
}
}
fn get_atom(string_rep: String) -> atom.Atom {
case atom.from_string(string_rep) {
Ok(atm) -> atm
Error(atom.AtomNotLoaded) -> atom.create_from_string(string_rep)
}
}
fn decode_inner_foo(
_dyn_value: dynamic.Dynamic,
) -> Result(InnerFooType, List(dynamic.DecodeError)) {
// bunch of decoding logic
// ...
Ok(InnerFooType)
}
fn decode_inner_bar(
_dyn_value: dynamic.Dynamic,
) -> Result(InnerBarType, List(dynamic.DecodeError)) {
// bunch of decoding logic
// ...
Ok(InnerBarType)
}
@external(erlang, "my_module", "my_erl_function")
fn my_priv_gleam_function(a: a, b: b) -> dynamic.Dynamic
@external(erlang, "my_module", "my_erl_function_using_undefined_behaviour")
fn my_priv_gleam_function_using_undefined_behaviour(a: a, b: b) -> MyNestedType There are probably good reasons why not, but right now I feel like I really want to have language support for atom litterals and a way to pattern match on dynamic.Dynamic. fn stuff_i_think_i_want(some_dynamic_value: dynamic.Dynamic) {
case some_dynamic_value {
#(:a_literal_atom, value) -> Ok(some_func(value))
_ -> Error("oh no!")
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 5 replies
-
Hello! Yes, the representation is part of the Gleam's API. Atoms will not be added to the language, they encourage very poor type design (being strings at the end of the day) and should be kept as an implementation detail. |
Beta Was this translation helpful? Give feedback.
Hello! Yes, the representation is part of the Gleam's API.
Atoms will not be added to the language, they encourage very poor type design (being strings at the end of the day) and should be kept as an implementation detail.