#Looking for feedback on decoders

1 messages · Page 1 of 1 (latest)

green crag
#

Hey, I was experimenting with different APIs for decoders (e.g. JSON parsers). I have found a way to write decoders in a very clean and understandable way. I am looking for feedback on the API and maybe some other ideas.

https://github.com/Hackder/decoders_gleam/blob/master/test/dec_test.gleam
This is a showcase of the api. The repo contains more experiments and also a solution by @glass osprey which he showcased on stream recently. (I copy pasted to play around with it).

The criteria for creating my decoders was:

  • Locality of behavior - single property defined on a single line.
  • Reports all errors

I first created buch of monadic parsers (exits on first error). Those are also in the repo. May be worth to consider.

Thanks to everyone who finds the time to give some feedback 🙂

GitHub

Contribute to Hackder/decoders_gleam development by creating an account on GitHub.

azure vapor
#

Looks easy to use

warm heart
#

How does it report all errors? The api looks monadic

noble peak
#

Those look nice!

green crag
warm heart
#

Ah I see, but then I can't depend on the intermediate values until I build the whole thing, right?

use tag <- field("tag", string)
//  ^^^ Here I might get a default value so I can't safely rely on it
//      actually being decoded?
case tag {
  "wibble" -> decode_wibble()
  "wobble" -> decode_wobble()
  _ -> invalid_tag()
}
glass osprey
#

Yeah and there’s a gotcha where the constructor is run even if it’s an error

green crag
#

Yes, that is right. It is a tradeof of the implementation.

glass osprey
#

So be sure not to do something like try to insert into the database after a series of “use” decoding things

green crag
#

Of course. It is defninitely not a perfect solution, but the only one that I was able to find and that satisfied both criteria:

  • single line for single property
  • returns all errors

I don't see a way of avoiding the default values in this kind of API without falling back to monadic

#

The default way seems to be doing both, but the api is cluky to use

#

Mabe a manual curry operator would fix all our problems? 😆

glass osprey
#

That’s what use is, that’s the current theory at least

green crag
#

Yes, it's good, but it requires you to keep the order of the use name <- parameter calls the same in the "curry" part and the "field(..)" part.

Maybe something like:

type User {
  User(name: String, age: Int)
}

fn main() {
  let curried = User!  // Curries the function compile time
}
#

Although I am not really sure if it's a great idea

glass osprey
#

That still requires you to get the order right

green crag
#

Yes, but only once, not twice

glass osprey
#

Same number of times, with use you can use labels

green crag
#

Imo this is due to gleam having "type constructors" which behave like functions. In other languages, it's common to have { name: the_name } and the { name } is syntactic sugar (as per js).

green crag
glass osprey
#

Type constructors such as Option do not behave like functions, they are not values

#

Record constructors such as Some are literally functions

glass osprey
green crag
#

Right. But as per the decoder you showcased on stream recently, this only solves the order in the "curry" part. When you are piping it to the field functions, you still have to get the order of those right. Or am I missing something?

glass osprey