{"id":19502366,"url":"https://github.com/zio-archive/zio-deriving","last_synced_at":"2025-04-26T00:31:51.461Z","repository":{"id":38419223,"uuid":"484570638","full_name":"zio-archive/zio-deriving","owner":"zio-archive","description":null,"archived":true,"fork":false,"pushed_at":"2023-10-23T17:11:09.000Z","size":104,"stargazers_count":11,"open_issues_count":12,"forks_count":3,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-03-23T05:41:41.250Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://zio.dev/zio-deriving","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zio-archive.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-22T21:01:55.000Z","updated_at":"2025-01-20T13:05:18.000Z","dependencies_parsed_at":"2023-02-05T04:00:34.855Z","dependency_job_id":"7cac9150-acd5-48c2-b3ef-c638d721a098","html_url":"https://github.com/zio-archive/zio-deriving","commit_stats":null,"previous_names":["zio-archive/zio-deriving"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zio-archive%2Fzio-deriving","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zio-archive%2Fzio-deriving/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zio-archive%2Fzio-deriving/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zio-archive%2Fzio-deriving/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zio-archive","download_url":"https://codeload.github.com/zio-archive/zio-deriving/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250917284,"owners_count":21507561,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T22:16:13.489Z","updated_at":"2025-04-26T00:31:51.167Z","avatar_url":"https://github.com/zio-archive.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"[//]: # (This file was autogenerated using `zio-sbt-website` plugin via `sbt generateReadme` command.)\n[//]: # (So please do not edit it manually. Instead, change \"docs/index.md\" file or sbt setting keys)\n[//]: # (e.g. \"readmeDocumentation\" and \"readmeSupport\".)\n\n# ZIO Deriving\n\n# Summary\n\nZIO Deriving is a Scala library for typeclass derivation with the design goals of:\n\n- **compatibility** source compatible with both Scala 2 and Scala 3.\n- **simple** the implementation has minimal macros and avoids type-level programming.\n- **fast compilations** never slower than manual instances.\n- **fast runtime** minimal overhead compared to manual instances.\n\nThe remainder of this document may be considered a standalone companion to [Functional Programming for Mortalz](https://leanpub.com/fpmortals).\n\n## What is a Typeclass?\n\nTypeclasses are a way to encode polymorphism, i.e. functions that work for a variety of different types.\n\nA typeclass is a `trait` that:\n\n- holds no state\n- has a type parameter\n- has at least one abstract method\n- has laws\n- may contain generalised methods\n- may extend other typeclasses\n- has one implementation for each concrete type\n\n### Use in the Standard Library\n\nThe most visible example of a typeclass in the Scala standard library is the abstraction over numbers:\n\n```scala\npackage scala.math\n\ntrait Ordering[T] {\n  def compare(x: T, y: T): Int\n\n  def lt(x: T, y: T): Boolean = compare(x, y) \u003c 0\n  def gt(x: T, y: T): Boolean = compare(x, y) \u003e 0\n}\n\ntrait Numeric[T] extends Ordering[T] {\n  def plus(x: T, y: T): T\n  def times(x: T, y: T): T\n  def negate(x: T): T\n  def zero: T\n\n  def abs(x: T): T = if (lt(x, zero)) negate(x) else x\n}\n```\n\nWe can see all the key features of a typeclass in action:\n\n- there is no state\n- `Ordering` and `Numeric` have type parameter `T`\n- `Ordering` has abstract `compare` and `Numeric` has abstract `plus`, `times`, `negate` and `zero`\n- `Ordering` defines generalised `lt` and `gt` based on `compare`,\n  `Numeric` defines `abs` in terms of `lt`, `negate` and `zero`\n- `Numeric` extends `Ordering`\n- there is only one `Numeric[Int]`\n\nWe can now write functions for types that \"have a\" `Numeric` typeclass:\n\n```scala\ndef signOfTheTimes[T](t: T)(implicit N: Numeric[T]): T = {\n  import N._\n  times(negate(abs(t)), t)\n}\n```\n\nWe are no longer dependent on the OOP hierarchy of our input types, i.e. we don't demand that our input \"is a\" `Numeric`, which is vitally important if we want to support a third party class that we cannot redefine.\n\nBut the syntax for `signOfTheTimes` is clunky, there are some things we can do to clean it up. Introducing `ops` on the typeclass companion:\n\n```scala\nobject Numeric {\n  object ops {\n    implicit class NumericOps[T](t: T)(implicit N: Numeric[T]) {\n      def +(o: T): T = N.plus(t, o)\n      def *(o: T): T = N.times(t, o)\n      def unary_-: T = N.negate(t)\n      def abs: T = N.abs(t)\n\n      // duplicated from Ordering.ops\n      def \u003c(o: T): T = N.lt(t, o)\n      def \u003e(o: T): T = N.gt(t, o)\n    }\n  }\n}\n```\n\nBy also using `implicit` *context bounds* we can now write:\n\n```scala\nimport Numeric.ops._\n\ndef signOfTheTimes[T: Numeric](t: T): T = -(t.abs) * t\n```\n\n### Typeclass Derivation\n\nTypeclasses are *wired up* using the `implicit` language features.\n\nAn *instance* of `Ordering` is defined as an `implicit val` that implements the typeclass, and can provide faster implementations for the generalised methods (but they still box primitives, so aren't optimal):\n\n```scala\nimplicit val OrderingDouble: Ordering[Double] = new Ordering[Double] {\n  def compare(x: Double, y: Double): Int = java.lang.Double.compare(x, y)\n  override def lt(x: Double, y: Double): Boolean = x \u003c y\n  override def gt(x: Double, y: Double): Boolean = x \u003e y\n}\n```\n\nwith `implicit def` provided for most stdlib collections\n\n```scala\nimplicit def seqOrdering[CC[X] \u003c: Seq[X], T: Ordering]: Ordering[CC[T]] = ...\n```\n\nThe process of generating typeclass instances from `implicit def` rules is what we mean when we say *typeclass derivation*.\n\nTo keep things nice and simple, and to avoid going into the *implicit scope* rules of the compiler, these derivations typically live on the companion object of the typeclass or data type (to do otherwise is to create an \"orphan\" and they are very difficult to reason about but are useful for library interop).\n\nSay we create a simple data type like\n\n```scala\ncase class Complex[A](r: A, i: A)\n```\n\nthere is no way to automatically generate an `Ordering` instance, we must write one explicitly\n\n```scala\nobject Complex {\n  implicit def ordering[A: Ordering]: Ordering[Complex[A]] = new Ordering[Complex[A]] {\n    def compare(x: Complex[A], y: Complex[A]): Int = ...\n  }\n}\n```\n\nThat's where libraries such as `shapeless`, `magnolia` and `scalaz-deriving` come in. They allow users to write something closer to\n\n```scala\nobject Complex {\n  implicit def ordering[A: Ordering]: Ordering[Complex[A]] = Ordering.derived\n}\n```\n\n`zio-deriving` is an alternative approach; the remainder of this document will explain how to create derivation rules for typeclasses using zio-deriving.\n\n## Divide and Conquer\n\n`zio-deriving` defines some lawful user-facing typeclasses that abstract over typeclasses 🤯\n\n```scala\ntrait XFunctor[F[_]] {\n  def xmap[A, B](fa: F[A])(f: A =\u003e B, g: B =\u003e A): F[B]\n}\n\ntrait Align[F[_]] {\n  def align[A, B](fa: F[A], fb: F[B]): F[(A, B)]\n}\n\ntrait Decide[F[_]] {\n  def decide[A, B](fa: F[A], fb: F[B]): F[Either[A, B]]\n}\n```\n\nLet's take a moment to read those type signatures. Your typeclass is the `F[_]`. So they read like:\n\n- if you give me a typeclass for an `A`, and a way to convert an `A` into a `B` and a `B` into an `A`, then I can give you a typeclass for a `B`.\n- if you give me a typeclass for an `A` and a typeclass for a `B`, then I can give you a typeclass for a tuple of `A` and `B`.\n- if you give me a typeclass for an `A` and a typeclass for a `B`, then I can give you a typeclass for either `A` or `B`.\n\nThe laws are:\n\n- identity: `fa == xmap(fa)(id, id)`\n- composition: `xmap(xmap(fa, f1, g1), f2, g2) == xmap(fa, f2 . f1, g1 . g2)`\n- associativity (align): `align(align(fa, fb), fc) == align(fa, align(fb, fc))`\n- associativity (decide): `decide(decide(fa, fb), fc) == decide(fa, decide(fb, fc))`\n\n`zio-deriving` provides conveniences, to help implementing `XFunctor`\n\n```scala\ntrait Covariant[F[_]] extends XFunctor[F] {\n  def fmap[A, B](fa: F[A])(f: A =\u003e B): F[B]\n}\n\ntrait Contravariant[F[_]] extends XFunctor[F] {\n  def contramap[A, B](fa: F[A])(f: B =\u003e A): F[B]\n}\n```\n\n- if you give me a typeclass for an `A`, and a way to convert an `A` into a `B`, then I can give you a typeclass for a `B`.\n- if you give me a typeclass for an `A`, and a way to convert a `B` into an `A`, then I can give you a typeclass for a `B`.\n\nWe can refer to these as the AC/DC typeclasses.\n\nTo get automatic derivation for any `case class` the typeclass author implements `Align`. For `sealed trait` they implement `Decide`, then mix `Derivable` into the companion. Here's the implementation for `Ordering`\n\n```scala\nimplicit val xfunctor: Contravariant[Ordering] = new Contravariant[Ordering] {\n  def contramap[A, B](fa: Ordering[A])(f: B =\u003e A): Ordering[B] = new Ordering[B] {\n    def compare(x: B, y: B): Int = fa.compare(f(x), f(y))\n  }\n}\n```\n\n```scala\nimplicit val align: Align[Ordering] = new Align[Ordering] {\n  def align[A, B](fa: Ordering[A], fb: Ordering[B]): Ordering[(A, B)] = new Ordering[(A, B)] {\n    def compare(x: (A, B), y: (A, B)): Int = {\n      val xs = fa.compare(x._1, y._1)\n      if (xs != 0) xs\n      else fb.compare(x._2, y._2)\n    }\n  }\n}\n```\n\n```scala\nimplicit val decide: Decide[Ordering] = new Decide[Ordering] {\n  def decide[A, B](fa: Ordering[A], fb: Ordering[B]): Ordering[Either[A, B]] = new Ordering[Either[A, B]] {\n    def compare(x: Either[A, B], y: Either[A, B]): Int = (x, y) match {\n      case (Left(xa), Left(ya)) =\u003e fa.compare(xa, ya)\n      case (Right(xb), Right(yb)) =\u003e fb.compare(xb, yb)\n      case (Left(_), Right(_)) =\u003e -1\n      case (Right(_), Left(_)) =\u003e 1\n    }\n  }\n}\n```\n\nDownstream users just need to type\n\n```scala\ncase class Complex[A](r: A, i: A)\nobject Complex {\n  implicit def ordering[A: Ordering]: Ordering[Complex[A]] = Ordering.derived\n}\n```\n\nwhich can be simplified further in Scala 3 to\n\n```scala\ncase class Complex[A](r: A, i: A) derives Ordering\n```\n\nAnd it's not just limited to case classes of 2 parameters, it works for all arities and sealed traits.\n\n```scala\nsealed trait Dimension\ncase class Cube(x: Double, y: Double, z: Double) extends Dimension\ncase class Tesseract(x: Double, y: Double, z: Double, t: Double) extends Dimension\n```\n\nThe tests include a few more examples, which are good exercises. Try implementing `XFunctor`, `Align` and `Decide` for\n\n```scala\n  trait Equal[A]  {\n    // type parameter is in contravariant (parameter) position\n    def equal(a1: A, a2: A): Boolean\n  }\n\n  trait Default[A] {\n    // type parameter is in covariant (return) position\n    def default: Either[String, A]\n  }\n\n  trait Semigroup[A] {\n    // type parameter is in both covariant and contravariant position (invariant)\n    def add(a1: A, a2: A): A\n  }\n```\n\nHomework:\n\n- We can't implement `Decide[Semigroup]`, why not ?\n- why should we not implement `Decide[Arbitrary]` (as in ScalaCheck / ScalaProps) ?\n- what about case classes with no parameters, and case objects ?\n\n## Lower Level\n\n`zio-deriving` is just a bunch of generated code. Typeclass authors can use that mechanism directly if they can't write lawful AC/DC instances.\n\n`zio-deriving` data types mirror cases classes and sealed traits of all shapes (hence the name!)\n\n```scala\nsealed trait Shape[A]\nsealed trait CaseClass[A] extends Shape[A] { def value(i: Int): Any }\nsealed trait SealedTrait[A] extends Shape[A] { def value: A ; def index: Int }\n```\n\n```\ncase class CaseClass0[A]() extends CaseClass[A]\ncase class CaseClass1[A, A1](_1: A1) extends CaseClass[A]\ncase class CaseClass2[A, A1, A2](_1: A1, _2: A2) extends CaseClass[A]\n...\ncase class CaseClass64[A, A1, A2, ...](_1: A1, _2: A2, ...) extends CaseClass[A]\n```\n\n```scala\nsealed trait SealedTrait1[A, A1 \u003c: A] extends SealedTrait[A]\nsealed trait SealedTrait2[A, A1 \u003c: A, A2 \u003c: A] extends SealedTrait[A]\n...\nsealed trait SealedTrait64[A, A1 \u003c: A, A2 \u003c: A, ...] extends SealedTrait[A]\n\nobject SealedTrait {\n  case class _1[A, ...](value: A1) extends ...\n  case class _2[A, ...](value: A2) extends ...\n  ...\n  case class _64[A, ...](value: A64) extends ...\n}\n```\n\nThe conversion between regular data types and the zio-deriving shapes is handled by a typeclass that has a macro that automatically creates instances of a\n\n```scala\ntrait Shapely[A, B \u003c: Shape[A]] {\n  def to(a: A): B\n  def from(b: B): A\n}\n```\n\nwhere `A` is your own case classes and sealed traits, `B` is a `zio-deriving.Shape`.\n\nThe typeclass law is that roundtripping recovers an equal value\n\n- identity: `to(from(b)) == b` AND `from(to(a)) == a`\n\nThe `Derivable` trait provides all the boilerplate to apply a \"divide and conquer\" approach for all arities, into the tuple and `Either` that were implemented in `Align` and `Decide`.\n\nTypeclass authors can skip AC/DC and write codegen directly in their build tool, it's easy!\n\nTwo reasons to do this are: if the typeclass can't be expressed as a lawful AC/DC, or maximal performance is required.\n\nLet's write a typeclass and codegen the derivation rules for it in sbt. We want a way to get all the `case object` values that extend a `sealed trait`, a fairly standard enumeration encoding in Scala 2. Our typeclass is\n\n```scala\ntrait Enum[A] { self =\u003e\n  def values: List[A]\n\n  final def map[B](f: A =\u003e B): Enum[B] = new Enum[B] {\n    def values: List[B] = self.values.map(f)\n  }\n}\n```\n\nand we would want to implement it for `SealedTrait` something like this, hardcoded for sealed traits of 2 case objects\n\n```scala\nimplicit def sealedtrait2[A, A1 \u003c: A, A2 \u003c: A](\n  implicit A1: ValueOf[A1], A2: ValueOf[A2]\n) = new Enum[SealedTrait2[A, A1, A2]] {\n  def values = _1(A1.value) :: _2(A2.value) :: Nil\n}\n```\n\nwhich we then need to convert into codegen rules. `project/ExamplesCodeGen.scala` contains the full code, like\n\n```scala\n    val enums = (1 to sum_arity).map { i =\u003e\n      val tparams = (1 to i).map(p =\u003e s\"A$p \u003c: A\").mkString(\", \")\n      val tparams_ = (1 to i).map(p =\u003e s\"A$p\").mkString(\", \")\n      val implicits = (1 to i).map(p =\u003e s\"A$p: ValueOf[A$p]\").mkString(\", \")\n      val tycons = s\"SealedTrait$i[A, $tparams_]\"\n      val work = (1 to i).map { p =\u003e s\"_$p(A$p.value)\" }.mkString(\"\", \" :: \", \" :: Nil\")\n      s\"\"\"  implicit def sealedtrait$i[A, $tparams](implicit $implicits): Enum[$tycons] = new Enum[$tycons] {\n         |    def values: List[$tycons] = $work\n         |  }\"\"\".stripMargin\n    }\n    s\"\"\"package wheels.enums\n       |\n       |import zio-deriving._\n       |\n       |private[enums] trait GeneratedEnums {\n       |${enums.mkString(\"\\n\\n\")}\n       |}\"\"\".stripMargin\n```\n\nThere's not much more than `.map` and `.mkString` going on here.\n\nThe way I create codegen rules is to start by copy/pasting another example and changing the strings to match the template that I wrote by hand. It would be possible to create a custom DSL for the templates, much like the Haskell [`boilerplate`](https://hackage.haskell.org/package/boilerplate). But that is left as an exercise to the reader.\n\nWith all the `Enum` specific stuff stripped out, that template looks like\n\n```scala\n    val sealedtraits = (1 to 64).map { i =\u003e\n      val tparams = (1 to i).map(p =\u003e s\"\").mkString(\"\")\n      val implicits = (1 to i).map(p =\u003e s\"\").mkString(\"\")\n      s\"\"\n    }\n    s\"\"\n```\n\nHomework:\n\n- implement `Arbitrary` derivation rules for sealed traits\n\n## Meta\n\nSometimes our typeclasses might need access to more information than just the types.\n\nThat's where `Meta` helps:\n\n```scala\ntrait Meta[A] {\n  def name: String\n  def annotations: List[Annotation]\n  def fieldNames: Array[String]\n  def fieldAnnotations: Array[List[Annotation]]\n}\n```\n\nwhich is provided by a macro. A typical usecase for this is to implement an encoder or decoder.\n\nI rewrote `zio-json` to use zio-deriving and this is how an `Encoder` is able to get the field names for case classes with annotations providing overrides\n\n```scala\nabstract class CaseClassEncoder[A, CC \u003c: zio.deriving.CaseClass[A]](M: zio.deriving.Meta[A]) extends Encoder[CC] {\n  val names: Array[String] = M.fieldAnnotations\n    .zip(M.fieldNames)\n    .map {\n      case (a, n) =\u003e a.collectFirst { case field(name) =\u003e name }.getOrElse(n)\n    }\n    .toArray\n  ...\n}\n```\n\nsimilarly, we can pick a different encoder based on some annotations\n\n```scala\n  implicit def sealedtrait2[A, A1 \u003c: A, A2 \u003c: A](\n    implicit M: Meta[A], M1: Meta[A1], M2: Meta[A2],\n             A1: Encoder[A1], A2: Encoder[A2]\n  ): Encoder[SealedTrait2[A, A1, A2]] = {\n    M.annotations.collectFirst { case discriminator(n) =\u003e n } match {\n      case None =\u003e ...\n      case Some(hintfield) =\u003e ...\n    }\n  }\n```\n\nIt is really easy to encode user-specified customisations with annotations instead of complex `implicit` machinery. Here we can see 3 annotations being used by a user to customise the form of their JSON\n\n```scala\n@discriminator(\"hint\")\nsealed abstract class Parent\n\n@hint(\"Cain\")\ncase class Child1() extends Parent\n\n@hint(\"Abel\")\ncase class Child2(@field(\"lamb\") sheep: Int) extends Parent\n```\n\nNote here that we're able to get the `Meta` for every value in the sealed trait. That's not something that is easy to get hold of with shapeless or Magnolia and it opens up lots of possibilities for typeclass authors. In the case of `zio-json` it allows additional performance optimisations and security protection because we can skip over fields that are not going to be relevant to any of the subtypes.\n\n## Lazy\n\nThis covers an advanced topic and you might never need to know about this.\n\nIn Scala 2.13 and newer it is possible to use by-name implicit parameters\n\n```scala\ndef foo(implicit =\u003ebar: Bar): Baz = ...\n```\n\nmeaning that the `bar` is only evaluated when it is needed. It is a compiler warning in earlier versions of Scala and all implicit parameters are strictly evaluated. Normally that is not a problem, but think about what happens when deriving a typeclass for a data model such as\n\n```scala\nsealed trait ATree\ncase class Leaf(value: String) extends ATree\ncase class Branch(roots: List[ATree]) extends ATree\n```\n\nIf we were to ask for implicit evidence for a typeclass called `Nuthin` like this\n\n```scala\nobject ATree {\n  implicit val nuthin: Nuthin[ATree] = Nuthin.derived\n}\nobject Leaf {\n  implicit val nuthin: Nuthin[Leaf] = Nuthin.derived\n}\nobject Branch {\n  implicit val nuthin: Nuthin[Branch] = Nuthin.derived\n}\n```\n\nthen we'd get into a problem because `Branch.nuthin` depends on `ATree.nuthin`, and `ATree.nuthin` depends on `Branch.nuthin` : a cyclic dependency. The Scala compiler accepts cycles and at runtime we get a `NullPointerException`.\n\nYou may think that a possible fix would be to use `lazy val` instead of `val`, but for reasons beyond my understanding, that results in infinite recursion. Sometimes.\n\nThe safest workaround is to use a `lazy val` on the `sealed trait` and `def` on the subtypes that refer to the parent\n\n```scala\nobject ATree {\n  implicit lazy val nuthin: Nuthin[ATree] = Nuthin.derived\n}\nobject Leaf {\n  implicit val nuthin: Nuthin[Leaf] = Nuthin.derived\n}\nobject Branch {\n  implicit def nuthin: Nuthin[Branch] = Nuthin.derived\n}\n```\n\nan alternative encoding of the same idea (but hiding the subtype instances) is\n\n```scala\nobject ATree {\n  implicit lazy val nuthin: Nuthin[ATree] = {\n    implicit def leaf: Nuthin[Leaf] = Nuthin.derived\n    implicit def branch: Nuthin[Branch] = Nuthin.derived\n    Nuthin.derived\n  }\n}\n```\n\nBut that can still cause a problem if the `derived` rule is evaluating all its dependencies. And since implicit parameters are strictly evaluated, that's exactly what would happen.\n\nA simple fix is to use by-name implicit parameters in your generated code. However, that doesn't work for Scala 2.12 and earlier.\n\nA workaround is provided by\n\n```scala\nfinal class Lazy[A] private (private[this] var eval: () =\u003e A) {\n  lazy val value: A = {\n    val value0 = eval()\n    eval = null\n    value0\n  }\n}\nobject Lazy extends LazyCompat {\n  def apply[A](a: =\u003eA): Lazy[A] = new Lazy[A](() =\u003e a)\n}\n```\n\nwhich can be used to turn any parameter into a by-name one, but adding the extra benefit of caching and not holding a reference to the calculation.\n\nWhen you ask for an implicit `Lazy[Foo]` on Scala 2.13 or later, it will automatically use a by-name implicit. On Scala 2.12 or earlier, a macro ensures that the value of the implicit is calculated lazily, converting the call site into `Lazy(implicitly[Foo])` instead of `implicitly[Lazy[Foo]]`.\n\nUnlike `shapeless.Lazy`, this one doesn't do anything funky with the compiler.\n\nSo when generating typeclasses, make sure to wrap the dependencies with `Lazy`. The JSON example above is more like\n\n```scala\n  implicit def sealedtrait2[A, A1 \u003c: A, A2 \u003c: A](\n    implicit M: Meta[A], M1: Meta[A1], M2: Meta[A2],\n             A1: Lazy[Encoder[A1]], A2: Lazy[Encoder[A2]]\n  ) = ...\n```\n\nIf you look at the actual definitions of the AC/DC typeclasses, you'll see that they are all by-name, to accomodate for this. Implementators may want to cache the values in a `lazy val`. Another design choice for zio-deriving would be to use `Lazy` in the AC/DC API but it felt like pollution.\n\n## Documentation\n\nLearn more on the [ZIO Deriving homepage](https://zio.dev/ecosystem/)!\n\n## Contributing\n\nFor the general guidelines, see ZIO [contributor's guide](https://zio.dev/about/contributing).\n\n## Code of Conduct\n\nSee the [Code of Conduct](https://zio.dev/about/code-of-conduct)\n\n## Support\n\nCome chat with us on [![Badge-Discord]][Link-Discord].\n\n[Badge-Discord]: https://img.shields.io/discord/629491597070827530?logo=discord \"chat on discord\"\n[Link-Discord]: https://discord.gg/2ccFBr4 \"Discord\"\n\n## License\n\n[License](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzio-archive%2Fzio-deriving","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzio-archive%2Fzio-deriving","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzio-archive%2Fzio-deriving/lists"}