Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
Travis Brown
@travisbrown
(Where "application/json".type is shorthand for shapeless.Witness.`"application/json"`.T or whatever technique you're using to refer to the type-level string.)
Nicolas Rinaudo
@nrinaudo
I understand the general principle, but I'm not sure about using an explicit content type - this wouldn't be accessible anyway and could very well just be a random CsvTag tag for the exact same effect, wouldn't it?
I mean, you'd presumably type alias things - type CsvDecoder[A] = Decoder[CsvTag, A] and never even really expose CsvTag
well, you'd expose it, but callers would never need to know it exists
do you see the explicit content type as some sort of type-level documentation?
Travis Brown
@travisbrown
Mostly it's about not having to define types for every single possible format, but it has other advantages, like the fact that you don't have to worry about two different JSON libraries each having their own JsonTag.
Nicolas Rinaudo
@nrinaudo
on the other hand, I'd see the appeal of being able to provide default implementations - Decoder["text/*".type, A] that all text types could inherit from. There aren't that many ways to serialise an int. But I don't see how to get that to work
oh, I see
Travis Brown
@travisbrown
You can also get a runtime string from the typelevel string, which can be useful in some contexts. I'm not sure "text/*" is a good idea—I'd have to think about it.
Nicolas Rinaudo
@nrinaudo
right, using content-types is sort of an implicit enumeration, but not one that needs to be maintained in the code..
it probably is not a good idea, but it's true that some decoders are going to have lots of duplicated code. I have a CSV library, for instance, that defines a decoder for most primitive types, and another XPath one that defines almost exactly the same ones
the only difference is in the failure type and, if we assume content types at the type level, the content-type
One downside I see to the content-type approach is that it might not be specific enough. What exactly is an XML decoder? is it something that knows how to decode XML attributes, which surely is not quite the same thing as decoding results of XPath expressions, or an XML document itself?
Travis Brown
@travisbrown
You could have a type class that characterizes content types and provide generic codec instances for instances of that type class.
About your last question: the rule of thumb would be "if you'd serve some content using this content type, this decoder can decode it to type X".
Nicolas Rinaudo
@nrinaudo
I can't really come up with a concrete example, but I can't help but feel that this might be a bit too fuzzy - you could easily end up mixing instances from different APIs.
that's part of the beauty of your idea, really: a Decoder instance for a give content type would have the same type, regardless of the library that implements it
but you might find yourself having part of your JSON document extracted by circe instances and others by Argonaut ones, if you have weird dependencies, and I feel this might not be desirable
Travis Brown
@travisbrown
I think that's an issue that's best solved at other levels. I write my code generically with no constraints other than the need for a Decoder["application/json".type, Foo] instance, and it's the responsibility of callers to get an appropriate instance into scope.
…they could do that with import io.circe.generic.auto._ or by defining an Argonaut instance, but that doesn't (and shouldn't have to) matter to my code.
Nicolas Rinaudo
@nrinaudo
ok. I need to play with this a bit, get my head around the concept, but I appreciate you taking the time to discuss this. It certainly not something I considered - nor something I could have considered, really, it's the first time the idea of using phantom types to specialise type class instances even registered as something that one could do
in all your example, you only have 2 type parameters - content-type and decoded type. Does that mean that you're expecting all Decoders to work from in-memory strings, or is that just a shortcut?
Travis Brown
@travisbrown
It's shorthand, but so far that's the way both circe and Finch work.
circe's codec type will also have an additional configuration type parameter very soon (something we need to help guide generic derivation, among other things—I've got a blog post about it here).
Nicolas Rinaudo
@nrinaudo
I'm thinking of some of my use cases - XPath evaluation for example: it would be a shame to only be able to decode XML documents as Strings, as it would force you to parse the document once for each expression you want to evaluate.
let's say I'm scrapping some website and am interested in two different parts of a given page. I'd rather parse the thing as a Node once and evaluate my expressions on this
if that makes sense
Travis Brown
@travisbrown
Sorry, I misspoke—in both circe and Finch the Decoder has a fixed input type, but in circe it's a parsed JSON value. Neither is generic over the input type, but they do have different input types.
Nicolas Rinaudo
@nrinaudo
oh, I see
but they would need to become generic over the input type if they were to use the Decoder type class you have in mind, right?
Travis Brown
@travisbrown
Right.
Nicolas Rinaudo
@nrinaudo
thank you. That's quite a lot for me to digest, but it's certainly taken my brain in unusual directions
Travis Brown
@travisbrown
Thanks for the encouragement to start thinking about this stuff again!
Nicolas Rinaudo
@nrinaudo
wow, thanks for the phantom type idea, this is making the code so much easier to read and follow than my previous F-bound implementation!
laws in particular are becoming quite a bit clearer
Nicolas Rinaudo
@nrinaudo
not having a specialised Decoder per supported format does make it inconvenient to provide default instances though, there's no implicit context in which to stick them...
Travis Brown
@travisbrown
What do you mean? You could have a generic instance method that provides instances for any format.
Nicolas Rinaudo
@nrinaudo
mmm, I'm not sure I follow. When creating a type class instance, say, CsvDecoder, I usually stick CsvDecoder[Int], CsvDecoder[String]... in the CsvDecoder companion object
if Decoder becomes generic, something like Decoder[T, A] where T is the content type and A the decoded type, I have no specific companion object in which to put these default instances
Travis Brown
@travisbrown
Oh, got it. Yes, that's kind of inconvenient, but something like export-hook makes it less so.
Nicolas Rinaudo
@nrinaudo
right, but unless I'm mistaken, export-hook will make sure default instances are available at the right priority level, but will still require an explicit import
not the end of the world, but a bit inconvenient
Travis Brown
@travisbrown
Agreed.
Nicolas Rinaudo
@nrinaudo
of course, one way to work around that would to have the free T parameter not be a type level string but a singleton object, and put default instances in there. Not sure which is the lesser of the two evils there.
Travis Brown
@travisbrown
Ah, hadn't thought of that.
Nicolas Rinaudo
@nrinaudo
you lose the format unicity constraint that content-types were buying you though - you're all but guaranteed that you'll get different type tags for identical formats.
then again, if we use a fuller Decoder signature (Decoder[Encoded, Decoded, Tag]), you already have pretty good odds that different libraries will use different Encoded types...
Vladimir Kostyukov
@vkostyukov
I've started to working on something similar to this for Finch (finagle/finch#541) - only the encoder part though.
Nicolas Rinaudo
@nrinaudo
@vkostyukov that looks pretty amazing, but how did you solve the default instance problem? Does this code require an explicit import, or did you manage to sneak them in the implicit context?
and yes, it'd make sense that finch only need the Encoder part. Decoder would be more useful to featherbed, right?