#Regarding gleam/dynamic

1 messages · Page 1 of 1 (latest)

tranquil gazelle
#

Hey there! Purely out of curiosity I was looking at the gleam/dynamic HexDocs, and the module seems to have decoding function up to 9 values to decode. I’m just wondering, what do you do if you wish to decode more than 9 values from json into a record?
Thanks in advance! 🙌🏼🙌🏼🙌🏼

dark sinew
#

I think you need to define a custom dynamic function (or smth like that)

#

although idk for sure

heavy atlas
#

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

rotund ridge
heavy atlas
#

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:

  1. have a special compiler built-in that works in some special cases
  2. use code generation or other compile time metaprogramming
  3. use autocurrying
  4. YOLO and use unsafe null pointers
dire obsidian
heavy atlas
#
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

tranquil gazelle
#

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 :)

hard rivet
#

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

eternal iris
#

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 🤷

heavy atlas
#

That's the way to go for more than 9 imo

#

I would like something better though

hard rivet
#

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

heavy atlas
#

It's for user interfaces

#

The big two:

  1. JSON APIs
  2. 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

serene trellis
#

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:

  1. A record
pub type Person {
  User(name: String, age: String, friends: List(Person))
}
  1. 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)
  ]
}
  1. 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.

heavy atlas
#

We're in agreement, but we don't know of a way to do this, as we were discussing above

serene trellis
#

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.

heavy atlas
#

We don't have any good candidates presently

honest lion
#

but well, i guess it doesn't quite fit the requirement of not adding any possibly redundant features to the language...

heavy atlas
#

It's very limited and only solves one use of applicatives

#

So convenient for basic JSON but not very appealling overall

dark sinew
#

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?

clear arch
#

the list would require all elements to be the same type

#

which would not work for most cases

dark sinew
#

yeah, I thought of that

clear arch
#

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

dark sinew
#

uhmm, I tried reading it and couldn't understand it, lol

#

although I assume it's something like *args in python?

clear arch
#

Effectively being able to do this

fn add(original: #(...), elem: a) -> #(..., a) {
  #(...original, a)
}
dark sinew
#

that is... cursed

#

although I get it

clear arch
#

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

dark sinew
#

I think from the options lpil cited, codegen or macros seemed like a good one

hard rivet
#

I would like to see more people seriously explore codegen

clear arch
#

I'd love to but I can think of so many pitfalls that I'd have to overcome that it has discouraged me

hard rivet
#

But folks usually start quite bullish and then realise the problem is Hard and never mention it again 😅

clear arch
#

#off-topic message

#

this is my best solution without changing anything

#

and i hate it

#

(type safe and doesn't swallow errors)

dark sinew
clear arch
#

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

dark sinew
#

like, in any lang?

clear arch
#

oh

#

well i think so?

#

i don't know for sure as i don't have enough experience

hard rivet
#

Any lang works but presumably it would be most useful if it were written in gleam

clear arch
dark sinew
#

interesting...

#

maybe I'll try tackling it

#

although I have little experience so 🤷‍♂️

clear arch
#

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

hard rivet
#

Nop louis advises against constructing the glance ast for codegen purposes

clear arch
#

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

hard rivet
#

What library?

clear arch
hard rivet
#

I see

#

In any case the problem is not really the parsing or the codegen

clear arch
#

not at all, it's all the bits in the middle

hard rivet
#

Its how to handle non-primitive types

clear arch
#

yeppp, my best thought has been an interactive cli that searches through for a type signature that matches but i dunno

#

then you've got customisation (if desired)