#Regarding gleam/dynamic
1 messages · Page 1 of 1 (latest)
I think you need to define a custom dynamic function (or smth like that)
although idk for sure
You can use the result module
But then you only get 1 error rather than all of them
We have this thing called the applicative problem that makes it hard to generalise these operations to arbitrary number of items
Hayleigh and I have been looking for a good solution for some time with not much success
Would you be down to elaborate more? Genuinely curious
The basic idea is that we want to be able to combine multiple Result(t1, error), Result(t2, error), etc into a Result(#(t1, t2, ...), List(error)) but we can't find a good way to do this in Gleam
The only things we can find other languages do are:
- have a special compiler built-in that works in some special cases
- use code generation or other compile time metaprogramming
- use autocurrying
- YOLO and use unsafe null pointers
I don't have much experience with auto curried languages; how does it solve the problem?
decoding(User)
|> field("name", string)
|> field("score", int)
you can make an API like this
We have to have a decodeN function or call function.curryN
that's the best we have atm
This seems plausible and a nice functional, curried approach. Do the standalone decoding and field functions shown here come from the std library?
If worst comes to worst, we can just try to split up our data into smaller Records I guess? But if what you described works then that’s good enough for me, it’ll be interesting to see what implementation y’all look into regarding this in the future though :)
if you want to decode records with more than 9 fields i thinnk the best thing to do (albeit the ugliest) is to goo look at the source for decode9 and copy that into your project and expand it to the number you need
I've been doing hella
use some_prop <- result.try(dynamic.field("fieldname", dynamic.something)(input))
and then combining everything into the type at the end, because I don't care about collecting all errors.
It seems to work 🤷
to be honest i have rarely ever wanted to collect all the errors
for me it is firmly in the "theoretically nice, practically i have no use for it" category
It's for user interfaces
The big two:
- JSON APIs
- HTML Forms
If you don't return all the errors in these cases your UX degrades to misery
So I think it's very much a practical thing
I wouldn't bother if it wasn't for these two
A better decode API would be great for Gleam IMO as it enables better dx for two of the most important things a web service does which are validate user input, and talk to databases.
Let's say if a library like TS's Zod was implemented in Gleam (placeholder name of lib) it would require:
- A record
pub type Person {
User(name: String, age: String, friends: List(Person))
}
- A schema
pub fn person_schema() {
[
lib.string("name")
|> lib.min_length(2),
lib.int("age")
|> lib.min(18),
lib.array("friends", of: person_schema)
]
}
- A decoder
pub fn person_decoder(json_string: String) -> Result(Person, List(lib.ValidateError)) {
let decoder = dynamic.decode3(
Person,
field("name", of: dynamic.string),
field("age", of: dynamic.int),
field("nicknames", of: dynamic.list(string)),
)
lib.parse(from: json_string, using: decoder)
}
And/or an encoder. Not the end of the world but as the schema grows and changes keeping these elements in sync (especially because errors won't be caught at compile time) is challenging.
If there didn't need to be different decode functions for different aritys then the decoding could be done by just passing the schema to a lib.decode function.
Apologies if I'm over-focused on the end-user API, but in my mind this is a core use case for gleam so a solid api here could go a long way.
We're in agreement, but we don't know of a way to do this, as we were discussing above
Got it. Is there a solution that looks the most promising? The proposal for allowing tuple concatenation and spreading into functions seemed practical if going full curry isn’t right for the language.
We don't have any good candidates presently
something i really like about Mint is the decode/encode expression: https://mint-lang.com/guide/reference/javascript-interop/decode-expression
when combined with https://mint-lang.com/api/modules/Json it allows for really ergonomic usage: https://sandbox.mint-lang.com/sandboxes/2BJuKpLCtodAqw
but well, i guess it doesn't quite fit the requirement of not adding any possibly redundant features to the language...
It's very limited and only solves one use of applicatives
So convenient for basic JSON but not very appealling overall
couldn't you accept a list as an input (like decodeN(const, [t1, t2, ...])) and have it check if all of them are Ok and, if not, concatenate the errors?
the list would require all elements to be the same type
which would not work for most cases
yeah, I thought of that
There has been a proposal via tuples but I think there is a few issues with it
One of the issues here is that it would probably require lots of copying which isn't ideal for perf
uhmm, I tried reading it and couldn't understand it, lol
although I assume it's something like *args in python?
Effectively being able to do this
fn add(original: #(...), elem: a) -> #(..., a) {
#(...original, a)
}
What I wrote is slightly different to the linked proposal because I came up with the same idea separately
So there's a small difference in syntax but it's the same idea
I think from the options lpil cited, codegen or macros seemed like a good one
I would like to see more people seriously explore codegen
I'd love to but I can think of so many pitfalls that I'd have to overcome that it has discouraged me
But folks usually start quite bullish and then realise the problem is Hard and never mention it again 😅
#off-topic message
this is my best solution without changing anything
and i hate it
(type safe and doesn't swallow errors)
how would you think someone would start to tackle codegen? (like, in general)
The very first step is that you'd need to parse the type that you're generating the encoders/decoders for
The last step is generating the gleam code for the encoders/decoders
anything in the middle is up to you
like, in any lang?
Any lang works but presumably it would be most useful if it were written in gleam
https://github.com/lpil/glance for the parser if you're using gleam
interesting...
maybe I'll try tackling it
although I have little experience so 🤷♂️
there is a library often suggested for generating Gleam code but Louis advises against using it for that purpose I believe
so probably best handle that yourself I think
Nop louis advises against constructing the glance ast for codegen purposes
sorry that's what I meant
what i meant is that there is a library that does exactly that
and louis suggests against using it for that purpose
What library?
not at all, it's all the bits in the middle
Its how to handle non-primitive types