Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LSP: generate decoder for individual variants of multi variant type #4161

Open
giacomocavalieri opened this issue Jan 9, 2025 · 8 comments

Comments

@giacomocavalieri
Copy link
Member

The generate decoder for type action is really nice. I think it would be great if we could make it work with multi variant types as well. We don't know how one could represent the type as a whole so generating a single decoder is out of the question but it would be incredibly useful if we could generate single decoders for each of the variants:

pub type Wibble {
//        ^ [generate decoders for variants]
  Wobble(Int, String)
  Woo(label: Bool)
}

Would produce two separate decoders:

pub fn wobble_decoder() { ... }
pub fn woo_decoder() { ... }

Then I would be free to choose how to combine those to decode a Wibble as a whole, saving on a lot of typing.

Should the action trigger on the type saying "generate decoders for the variants" or should it trigger on a per-variant basis? I personally think it would be better to have it trigger on the type as a whole and generate all the decoders at once. I can't imagine a case in which I'd like to generate just a single decoder.

@GearsDatapacks
Copy link
Member

Yep, that was a thought I had as well. I think generating all decoders at once is good

@lpil
Copy link
Member

lpil commented Jan 9, 2025

At that point would it not make sense to have one decoder for all of them together? Surely that would be the next step for everyone.

@giacomocavalieri
Copy link
Member Author

giacomocavalieri commented Jan 9, 2025

How would it be implemented? Whatever that is, wouldn't we be nudging folks towards using the specific representation we pick?

pub type Wibble {
  Wobble(one: Int)
  Woo(string: String)
}

pub fn wibble_decoder() {
  // How would the LS combine the two basic decoders?
  // Let's say we went for `one_of`.
  decode.one_of(wobble_decoder(), [woo_decoder()])
}

pub fn wobble_decoder() { ... }
pub fn woo_decoder() { ... }

If I had the building blocks I could choose how to compose those the way it makes sense for me, maybe in my case I'm using a tag to tell the two variants apart and I'd just end up deleting the "default" decoder generated by the LS to write this:

pub fn wibble_decoder() {
  use tag <- decode.field("tag", tag_decoder())
  let inner_decoder = case tag {
    WobbleTag -> wobble_decoder()
    WooTag -> woo_decoder()
  }
  decode.at(["value"], inner_decoder)
}

fn tag_decoder() { ... }

@lpil
Copy link
Member

lpil commented Jan 18, 2025

I think that last one would be close to what we would have as the default format. We couldn't use the first one as it may not always be valid due to structurally overlapping variant field sets.

It wouldn't be always useful, but if it shares a format with a JSON encoder then we could enable people to more quickly create JSON servers and clients for their full stack Gleam.

@giacomocavalieri
Copy link
Member Author

In that case it would also have to generate a custom type for the tag though, and how should it behave if any of those names are already taken?

@lpil
Copy link
Member

lpil commented Jan 19, 2025

Why would we need to generate a custom tag for the tag? It would be a string in JSON and converting to and atom would be wasted work as we can compare strings.

@giacomocavalieri
Copy link
Member Author

giacomocavalieri commented Jan 19, 2025

Then we'd have to come up with a "zero" value to pass to the failing case:

pub fn wibble_decoder() {
  use tag <- decode.field("tag", tag_decoder())
  case tag {
    "wobble" -> decode.at(["value"], wobble_decoder())
    "woo" -> decode.at(["value"], woo_decoder())
    _ -> decode.failure(todo as "what do we put here?")
  }
}

So the compiler would have to come up with a zero value for that type instead of a zero value for a variant

@lpil
Copy link
Member

lpil commented Jan 20, 2025

That's also true for if we generate a custom type for the tag as deserialising that could fail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants