Closed gvolpe closed 3 years ago
@gvolpe
I don't think .asInstanceOf[p.Type] is a good idea here We could think of several possibilities
Here you don't need magnolia at all. You can demand newtype-only derivation
object keyEncoder extends Derivation[KeyEncoder] with NewTypeDerivation[KeyEncoder]{
def instance(implicit x: OnlyNewtypes): Nothing = x.absurd
}
object keyDecoder extends Derivation[KeyDecoder] with NewTypeDerivation[KeyDecoder]{
def instance(implicit x: OnlyNewtypes): Nothing = x.absurd
}
@implicitNotFound("use keyEncoder and keyDecoder annotations only for newtypes")
abstract final class OnlyNewtypes{
def absurd = Nothing
}
Actually I don't know exactly how to compile-time check for single-variabilty, I suppose it may involve shapeless or custom macro
But you have to use ctx.construct
to create such instances
I suppose we can define some general scheme, such as using some separators in the keys to derive variable
class keyDecoder(sep: String = "::") {
type Typeclass[T] = KeyDecoder[T]
def combine[T](ctx: CaseClass[KeyDecoder, T]): KeyDecoder[T] =
if (ctx.isObject) key => if (key == ctx.typeName.short) Some(ctx.rawConstruct(Seq.empty)) else None
else { key =>
val parts = key.split(sep)
if (parts.length != ctx.parameters.length) None
else ctx.constructMonadic(p => p.typeclass.apply(parts(p.index)))
}
def dispatch[T](ctx: SealedTrait[KeyDecoder, T]): KeyDecoder[T] =
key => ctx.subtypes.view.flatMap(_.typeclass(key)).headOption
def instance[T]: KeyDecoder[T] = macro Magnolia.gen[T]
}
object keyDecoder extends keyDecoder("::") with Derivation[KeyDecoder] with NewTypeDerivation[KeyDecoder]
class keyEncoder(sep: String = "::") {
type Typeclass[T] = KeyEncoder[T]
def combine[T](ctx: CaseClass[KeyEncoder, T]): KeyEncoder[T] =
if (ctx.isObject) obj => ctx.typeName.short
else { cc =>
ctx.parameters.view.map(p => p.typeclass(p.dereference(cc))).mkString("::")
}
def dispatch[T](ctx: SealedTrait[KeyEncoder, T]): KeyEncoder[T] =
obj => ctx.dispatch(obj)(sub => sub.typeclass(sub.cast(obj)))
def instance[T]: KeyDecoder[T] = macro Magnolia.gen[T]
}
object keyEncoder extends keyEncoder("::") with Derivation[KeyEncoder] with NewTypeDerivation[KeyEncoder]
This will handle most of basic cases with default separator as well as simple type for creating key encoders with custom separators
@Odomontois thanks a lot! Option 3 seems great, got it working but I have no idea how to make the keyEncoder
one simpler, any pointers?
Also, I have another one for Http4s' Query Params, and it's probably completely wrong even if it works 😄 If you have any spare time, I would appreciate if you could have a look: https://github.com/gvolpe/pfps-shopping-cart/blob/second-edition/modules/core/src/main/scala/shop/ext/http4s/queryParam.scala
I added a new Typeclass Derivation chapter to the book, which is centered around Derevo, would you be interested in giving it a proof-read once I have something presentable? It would mean a lot 😇
Oh you just updated the comment, nice! 😃
@gvolpe Sorry, unlike me you react so fast, I've updated my comment with more comprehensive solution
@gvolpe Yeah I would gladly pre-read your book!
That's great to hear, got an email to contact you? If you don't want to make it public, you can ping me first at hello at gvolpe dot com
.
@gvolpe odomontois@gmail.com
@Odomontois sorry to bother again, I was thinking, do you think these instances could be part of Derevo? It seems something very common to have. Happy to submit a PR with these implementations.
@gvolpe that would be great
Here we go :) https://github.com/tofu-tf/derevo/pull/257
AFAIU the derivations supported by
derevo
are the ones supported bycirce-derivation
, is that right? If so, then I guess it is not possible to deriveKeyDecoder
andKeyEncoder
instances without upstream support?Anyhow, my only need for now is to support derivation for these typeclasses for newtypes. Here's a simple example:
There are
KeyDecoder[UUID]
andKeyEncoder[UUID]
instances so this works but I wanted to go a bit further and get Derevo to do this for me. This is what I come up with.Do you see anything wrong?
I did something similiar for
KeyEncoder
but this one is weird because I need to return aString
and not sure what the empty case should be.This works but I wonder if there is any downside or something wrong in my implementations?
Appreciate your help, I've never used Magnolia before.