tofu-tf / derevo Goto Github PK
View Code? Open in Web Editor NEWMultiple instance derivations inside a single macro annotation
Home Page: https://manatki.org/docs/derevo.html
Multiple instance derivations inside a single macro annotation
Home Page: https://manatki.org/docs/derevo.html
import derevo.cats.{eqv, show}
import derevo.circe.magnolia.{decoder, encoder}
import derevo.derive
import io.estatico.newtype.macros.newtype
import io.circe.refined._
import org.mongodb.scala.bson.annotations.BsonProperty
import eu.timepit.refined.cats._
import derevo.reactivemongo.{bsonDocumentReader, bsonDocumentWriter}
object myObject {
@derive(decoder, encoder, eqv, show, bsonDocumentReader, bsonDocumentWriter)
@newtype case class Id(value: String)
@derive(decoder, encoder, eqv, show)
@newtype case class Name(value: String)
@derive(decoder, encoder, eqv, show)
case class MyOject(@BsonProperty("_id") id: Id, name: Option[Name])
}
could not find implicit value for parameter repr: reactivemongo.bson.BSONDocumentReader[String]
@derive(decoder, encoder, eqv, show, bsonDocumentReader, bsonDocumentWriter)
Started migrating the shopping-cart application and bumped into this issue
[error] brand.scala:35:4: Could not find io.circe.Decoder instance for shop.domain.brand.BrandId
[error] @derive(decoder, encoder)
[error] ^
Given by the following simplified example.
import java.util.UUID
import derevo.derive
import derevo.circe.{ decoder, encoder }
import io.estatico.newtype.macros.newtype
object brand {
@newtype @derive(decoder, encoder)
case class BrandId(value: UUID)
@newtype @derive(decoder, encoder)
case class BrandName(value: String) {
def toBrand(brandId: BrandId): Brand =
Brand(brandId, this)
}
@derive(decoder, encoder)
case class Brand(uuid: BrandId, name: BrandName)
}
On the other hand, this works without issues:
object brand {
@newtype case class BrandId(value: UUID)
object BrandId {
implicit val jsonEncoder: Encoder[BrandId] = deriving
implicit val jsonDecoder: Decoder[BrandId] = deriving
}
@newtype case class BrandName(value: String) {
def toBrand(brandId: BrandId): Brand =
Brand(brandId, this)
}
object BrandName {
implicit val jsonEncoder: Encoder[BrandName] = deriving
implicit val jsonDecoder: Decoder[BrandName] = deriving
}
@derive(decoder, encoder)
case class Brand(uuid: BrandId, name: BrandName)
}
It would be cool to have integration with the supertagged library, like with the newtype library.
At the current moment I'm doing something like this in my codebase:
trait encoder {
type Raw
type Tag <: this.type
implicit def liftedEncoder(implicit raw: Encoder[Raw]): Encoder[Raw @@ Tag] = LiftF[Encoder].lift
}
object Example extends TaggedType[String] with encoder
type Example = Example.Type
But I want to write something like this:
@derive(encoder)
object Example extends TaggedType[String] with encoder
type Example = Example.Type
To determine that the type is a TaggedType
it's sufficient to check that its companion extends supertagged.TaggedType0[T]
.
This is the unit test shown above.
This is where MetaData
case classes are.
This is MetaDataClient. This works fine. It's just the unit test doesn't pass.
This is the dependency list.
I am working on Weaver unit tests. My json doesn't have the bottom level field names. There are no frameRate
, resolution
, md5
, sha1
and productionId
etc in json string 2
. Any suggestions?
The example is circe's Encoder
->ObjectEncoder
Update circe-derivation to 0.12.0-M7 and add support for second macro parameter
example
object bsonDocumentWriter
extends PolyDerivation[BSONWriter[?, _ <: BSONValue], BSONDocumentWriter]
May be useful to be able to ignore meta/system fields while deriving Eq instances.
Compiling this:
@derive(functor)
trait Bar[A] {
def bar(x: Int): A
}
@SuppressWarnings(Array("deprecation")) // any annotation here
object Bar
leads to exception:
macro expansion has failed: scala.MatchError: List(abstract trait Bar[A] extends scala.AnyRef {
[error] /home/***.scala:4:2: exception during macro expansion:
[error] scala.MatchError: List(abstract trait Bar[A] extends scala.AnyRef {
[error] def bar(x: Int): A
[error] }, @new SuppressWarnings(Array("deprecation")) object Bar extends scala.AnyRef {
[error] def <init>() = {
[error] super.<init>();
[error] ()
[error] }
[error] }) (of class scala.collection.immutable.$colon$colon)
[error] at org.manatki.derevo.Derevo.deriveMacro(Derevo.scala:55)
[error] @derive(functor)
[error] ^
[error] one error found
Apologies if this was discussed before but I was wondering if you have considered renaming this? Even the examples in the README file show a qualified import derevo.cats.{ eq => eqv }
since trying to use @derive(eq)
yields the error missing argument list for method eq in class Object
.
I'd be happy to submit a PR if so :)
I have a local build going.
Can do PR if it's something you would consider merging.
It would be good to have an ability to alias mutliple derivations at once, so instead of etc
import com.sksamuel.avro4s.{Encoder, Decoder, SchemaFor}
@derive(Encoder, Decoder, SchemaFor)
case class FooBar(foo: String, bar: Int)
one can write (preferred)
@avro4sCodec
case class FooBar(foo: String, bar: Int)
or
@derive(avro4sCodec)
case class FooBar(foo: String, bar: Int)
The following code doesn't compile on scala 2.13.5 (local import of derevo.cats._):
import cats.Monoid
object AnnotationProblem {
import derevo.derive
import derevo.cats.{eqv, monoid, order, show}
@derive(eqv, show, order, monoid)
case class ABC(
a: String,
b: Int,
c: Boolean
)
val abcEmpty = Monoid[ABC].empty
}
Compilation error:
exception during macro expansion:
scala.reflect.macros.TypecheckException: not found: value eqv
at scala.reflect.macros.contexts.Typers.$anonfun$typecheck$3(Typers.scala:44)
...
https://scastie.scala-lang.org/mbelikov/RARK4uYyRsuV3tEtvnOhIQ/4
Putting the import of derevo.cats._ to the global scope solves the compilation problem:
https://scastie.scala-lang.org/mbelikov/RARK4uYyRsuV3tEtvnOhIQ/6
The issue seems to be scala 2.13.x specific, for the 2.12 compiler the local import is ok:
https://scastie.scala-lang.org/mbelikov/RARK4uYyRsuV3tEtvnOhIQ/7
Hi! It would be nice to have circe-generic-extras support. Also, Circe now has Codec
, which is both an Encoder
and Decoder
, and deriving it reduces compilation time compared to encoder+decoder.
Would be nice to see support for µPickle which looks like the following:
For case clasess:
import upickle.default.{ReadWriter => RW, macroRW}
case class Thing(myFieldA: Int, myFieldB: String)
object Thing{
implicit val rw: RW[Thing] = macroRW
}
case class Big(i: Int, b: Boolean, str: String, c: Char, t: Thing)
object Big{
implicit val rw: RW[Big] = macroRW
}
And for sealed traits:
sealed trait IntOrTuple
object IntOrTuple{
implicit val rw: RW[IntOrTuple] = RW.merge(IntThing.rw, TupleThing.rw)
}
case class IntThing(i: Int) extends IntOrTuple
object IntThing{
implicit val rw: RW[IntThing] = macroRW
}
case class TupleThing(name: String, t: (Int, Int)) extends IntOrTuple
object TupleThing{
implicit val rw: RW[TupleThing] = macroRW
}
Currently, derevo fails to derive, e.g. functorK, for traits, which are not in the form trait Foo[F[_]]{...}
Additional type parameters make it fail, for example, the following snippet:
@derive(functorK)
trait FooCache[F[_], K, V] {
def get(key: K): F[Option[V]]
def upsert(key: K, value: V): F[Unit]
def invalidate(key: K): F[Unit]
}
will result in
wrong number of type arguments for org.package.FooCache, should be 3
Derivation for case classes with parameters was broken in #30
Currently, there is no way to use type parameters inside derive
:
@derive(labeled["foo"]("bar1")) // Any does not take type parameters
Full example: https://scastie.scala-lang.org/LMnet/GkzZYusnSvSdnrwPWCSOYQ/10
Since 2020.3 Scala plugin for IntelliJ IDEA will use Scala 2.13. New version of library extension compiled with Scala 2.13 should be published.
java.lang.NoSuchMethodError: 'scala.collection.mutable.WrappedArray scala.Predef$.wrapRefArray(java.lang.Object[])'
at derevo.intellij.DerevoDeriveInjector$.<init>(DerevoDeriveInjector.scala:87)
at derevo.intellij.DerevoDeriveInjector$.<clinit>(DerevoDeriveInjector.scala)
at derevo.intellij.DerevoDeriveInjector.needsCompanionObject(DerevoDeriveInjector.scala:12)
at org.jetbrains.plugins.scala.lang.psi.impl.toplevel.typedef.SyntheticMembersInjector$.$anonfun$needsCompanion$1(SyntheticMembersInjector.scala:146)
at org.jetbrains.plugins.scala.lang.psi.impl.toplevel.typedef.SyntheticMembersInjector$.$anonfun$needsCompanion$1$adapted(SyntheticMembersInjector.scala:146)
at scala.collection.ArrayOps$.exists$extension(ArrayOps.scala:684)
at org.jetbrains.plugins.scala.lang.psi.impl.toplevel.typedef.SyntheticMembersInjector$.needsCompanion(SyntheticMembersInjector.scala:146)
...
AFAIU the derivations supported by derevo
are the ones supported by circe-derivation
, is that right? If so, then I guess it is not possible to derive KeyDecoder
and KeyEncoder
instances without upstream support?
Anyhow, my only need for now is to support derivation for these typeclasses for newtypes. Here's a simple example:
@derive(decoder, encoder, eqv, show, uuid)
@newtype
case class ItemId(value: UUID)
object ItemId {
implicit val keyEncoder: KeyEncoder[ItemId] = deriving
implicit val keyDecoder: KeyDecoder[ItemId] = deriving
}
There are KeyDecoder[UUID]
and KeyEncoder[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.
import derevo.{ Derivation, NewTypeDerivation }
import io.circe.KeyDecoder
import magnolia.{ CaseClass, Magnolia }
object keyDecoder extends Derivation[KeyDecoder] with NewTypeDerivation[KeyDecoder] {
type Typeclass[T] = KeyDecoder[T]
def combine[T](ctx: CaseClass[KeyDecoder, T]): KeyDecoder[T] = new KeyDecoder[T] {
def apply(key: String): Option[T] =
ctx.parameters.toList match {
case (p :: _) => p.typeclass.apply(key).map(_.asInstanceOf[T])
case _ => None
}
}
def instance[T]: KeyDecoder[T] = macro Magnolia.gen[T]
}
Do you see anything wrong?
I did something similiar for KeyEncoder
but this one is weird because I need to return a String
and not sure what the empty case should be.
object keyEncoder extends Derivation[KeyEncoder] with NewTypeDerivation[KeyEncoder] {
type Typeclass[T] = KeyEncoder[T]
def combine[T](ctx: CaseClass[KeyEncoder, T]): KeyEncoder[T] = new KeyEncoder[T] {
def apply(key: T): String =
ctx.parameters.toList match {
case (p :: _) => p.typeclass.apply(key.asInstanceOf[p.PType])
case _ => "error"
}
}
def instance[T]: KeyEncoder[T] = macro Magnolia.gen[T]
}
This works but I wonder if there is any downside or something wrong in my implementations?
@derive(decoder, encoder, keyDecoder, keyEncoder, eqv, show, uuid)
@newtype
case class ItemId(value: UUID)
Appreciate your help, I've never used Magnolia before.
data {
name = Demo
list = [1, 2, 3]
map.wtf = 1
map.lol = 2
map.wut = 3
rate {
elements = 2
duration = 100 millis
}
}
The above hocon sample from CirisSpec.scala
yields:
Data(Demo,List(3, 2, 1),Map(wut -> 3, lol -> 2, wtf -> 1),Rate(2))
Please suppress WartRemover warnings as it's done in simulacrum
:
https://github.com/mpilquist/simulacrum/blob/a88ff4dc7b5a67d312813e71a85fc32f178dbdc4/core/src/main/scala/simulacrum/typeclass.scala#L387
Not sure which particular warts should be suppressed. Adding only ExplicitImplicitTypes
worked for me, but the list might be bigger in other cases.
Usage of both derive and newtype annotations with unnapply parameter for newtype annotation
import io.estatico.newtype.macros.newtype
import derevo.derive
import derevo.circe.magnolia.encoder
object Main extends App {
object Foo {
@derive(encoder)
@newtype(unapply = true)
final case class Bar(val str: String)
}
}
leads to compilation error
It seems the @newtype
annotation from the scala-newtype library doesn't work well with the @derive
annotation.
This fails to compile:
@newtype
@derive(show)
case class Foo(value: String)
@newtype
@derive(show)
case class Bar(value: Int)
@derive(show)
final case class Ctx(foo: Foo, bar: Bar)
Here's the error:
[error] /examples/foo.scala:19:4: magnolia: could not find Show.Typeclass for type examples..Foo
[error] in parameter 'foo' of product type examples.Ctx
[error] @derive(show)
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
Maybe worth looking into scalaz-deriving that has support for it.
Currently the only way to specify a way to encode/decode values for derevo-circe's encoder
/decoder
is by passing fully qualified name as a string to macroannotation. It would be nice to support a more typesafe way of doing so, i.e. @derive(encoder(snakeCaseMemberNames))
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.