Skip to content

Kindlings logo

Kindlings

Hearth-powered sanely-automatic derivation.

Type class derivation that compiles faster, runs faster, and works the same on Scala 2.13 and Scala 3. Drop-in replacements for derivation in Circe, Jsoniter Scala, Avro, and more — built with Hearth, powered by macros, free of the trade-offs you've learned to accept.

Quick start

sbt

// derivations:
libraryDependencies += "com.kubuszok" %% "kindlings-avro-derivation" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-cats-derivation" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-circe-derivation" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-diff-derivation" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-fast-show-pretty" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-jsoniter-derivation" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-pureconfig-derivation" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-scalacheck-derivation" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-sconfig-derivation" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-tapir-schema-derivation" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-ubjson-derivation" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-yaml-derivation" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-xml-derivation" % "0.2.0"

// integrations:
libraryDependencies += "com.kubuszok" %% "kindlings-cats-integration" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-iron-integration" % "0.2.0"
libraryDependencies += "com.kubuszok" %% "kindlings-refined-integration" % "0.2.0"

// extra:
libraryDependencies += "com.kubuszok" %% "kindlings-jsoniter-json" % "0.2.0"

Scala CLI

// derivations:
//> using dep com.kubuszok::kindlings-avro-derivation:0.2.0
//> using dep com.kubuszok::kindlings-cats-derivation:0.2.0
//> using dep com.kubuszok::kindlings-circe-derivation:0.2.0
//> using dep com.kubuszok::kindlings-diff-derivation:0.2.0
//> using dep com.kubuszok::kindlings-fast-show-pretty-derivation:0.2.0
//> using dep com.kubuszok::kindlings-jsoniter-derivation:0.2.0
//> using dep com.kubuszok::kindlings-pureconfig-derivation:0.2.0
//> using dep com.kubuszok::kindlings-scalacheck-derivation:0.2.0
//> using dep com.kubuszok::kindlings-sconfig-derivation:0.2.0
//> using dep com.kubuszok::kindlings-tapir-schema-derivation:0.2.0
//> using dep com.kubuszok::kindlings-ubjson-derivation:0.2.0
//> using dep com.kubuszok::kindlings-yaml-derivation:0.2.0
//> using dep com.kubuszok::kindlings-xml-derivation:0.2.0

// integrations:
//> using dep com.kubuszok::kindlings-cats-integration:0.2.0
//> using dep com.kubuszok::kindlings-iron-integration:0.2.0
//> using dep com.kubuszok::kindlings-refined-integration:0.2.0

// extra:
//> using dep com.kubuszok::kindlings-jsoniter-json:0.2.0
Minimal example
//> using scala 2.13.18
//> using dep com.kubuszok::kindlings-circe-derivation:0.2.0
//> using dep io.circe::circe-parser:0.14.15

import hearth.kindlings.circederivation._
import io.circe._

case class Person(name: String, age: Int)

// inline encoding — no implicit needed
val json: Json = KindlingsEncoder.encode(Person("Alice", 30))
println(json.noSpaces)
// expected output:
// {"name":"Alice","age":30}

// inline decoding
val parsed = io.circe.parser.parse("""{"name":"Bob","age":25}""")
println(parsed.flatMap(KindlingsDecoder.decode[Person](_)))
// expected output:
// Right(Person(Bob,25))

Why Kindlings?

Most Scala libraries derive type class instances using Shapeless (Scala 2), Scala 3 Mirrors, or Magnolia. These approaches work, but they come with trade-offs that compound as your project grows: slow compilation, poor error messages, runtime overhead from intermediate representations, and API fragmentation between Scala 2 and Scala 3.

Kindlings takes a different path. Built on Hearth, it uses macros to generate code that is closer to what you'd write by hand — while providing a better developer experience than any of the alternatives.

One API across Scala 2.13 and Scala 3

No conditional imports, no platform-specific code. Your derivation calls look the same regardless of the Scala version. Migration between Scala 2.13 and Scala 3 requires zero changes to your derivation code.

Recursive derivation out of the box

Kindlings handles recursive data types without lazy wrappers, manual knot-tying, or tricks. No Lazy[_], no implicit lazy val, no special configuration — it just works.

case class Tree(value: Int, children: List[Tree])

// works with every Kindlings module — no special handling needed

Sanely-automatic derivation

In most libraries, you choose between automatic and semi-automatic derivation — and each comes with trade-offs. Automatic derivation (like import io.circe.generic.auto._) is convenient but re-derives instances at every call site, slowing compilation and sometimes producing worse runtime code. Semi-automatic derivation avoids that but requires explicit implicit val / given boilerplate for every type.

Kindlings eliminates this choice:

  • Semi-automatic is recursive. KindlingsEncoder.derived[Person] derives not just Person but also Address, List[Address], and anything else reachable — no need to define instances for nested types manually.
  • Automatic imposes no overhead. For a single derivation site, automatic and semi-automatic produce identical code — same compilation cost, same runtime performance. The generated code is as fast as what you'd write by hand.
  • Errors are informative and actionable. When derivation fails, you get a clear message telling you exactly which type is missing an instance and where in the hierarchy the problem is.

If the same type is auto-derived at multiple call sites, each site derives independently — but this is still cheaper than Shapeless/Mirrors-based automatic derivation, because Kindlings' macro expansion is lightweight by design.

For a deeper dive, see Sanely-automatic derivation.

Debug logging on demand

When derivation fails or you want to understand what the macro produces, enable debug logging with a single import:

import hearth.kindlings.circederivation.debug._

Or globally via a scalac option — no code changes needed:

-Xmacro-settings:circeDerivation.logDerivation=true

Every module supports both approaches. The output shows exactly what code the macro generates, which rules matched each field, and where summoned implicits came from.

Flame graph generation on demand

Profile macro compilation performance with Hearth's built-in flame graph support:

-Xmacro-settings:hearth.mioBenchmarkScopes=true
-Xmacro-settings:hearth.mioBenchmarkFlameGraphDir=/tmp/kindlings-flamegraphs

Generates .speedscope.json files you can visualize at speedscope.app — useful for diagnosing slow compilation in large projects.

Macro timeouts prevent runaway compilation

Every derivation module enforces a 5-second timeout by default. If a macro expansion takes longer than that, compilation fails with a clear error — no more silently waiting minutes for a derivation that will never finish.

If a specific type hierarchy legitimately needs more time, you can raise the limit per module:

-Xmacro-settings:circeDerivation.timeout=30s
-Xmacro-settings:jsoniterDerivation.timeout=1m

Supported formats: plain integer (seconds), Ns, Nms, Nm.

No imports for integration modules

Refined types, Iron types, and Cats collections work automatically. Just add the integration dependency to your build — no imports, no configuration. The macro extension system discovers them at compile time.

// build.sbt — just add the dependency
libraryDependencies += "com.kubuszok" %% "kindlings-refined-integration" % "0.2.0"

// your code — no extra imports, refined types just work
import eu.timepit.refined.api.Refined
import eu.timepit.refined.numeric.Positive

case class Order(quantity: Int Refined Positive, item: String)
// Circe/Jsoniter/Avro/... derivation handles Refined fields automatically

Comparison at a glance

Kindlings Shapeless / Mirrors Magnolia Library-specific macros
Same API on Scala 2.13 and 3 Yes No No varies
Auto derivation without overhead Yes No No varies
Inline derivation Yes No No some
Recursive types (no tricks) Yes needs semiauto + lazy val / Lazy Yes varies
Clear error messages Yes No partial varies
Code preview Yes No No rare
Named tuples, opaque types Yes No No rare
Scala 3 enums, Java enums Yes partial partial varies

Available modules

Module Replaces Derived type classes
kindlings-avro-derivation avro4s (JVM only) AvroSchemaFor, AvroEncoder, AvroDecoder
kindlings-cats-derivation kittens Show, Eq, Order, Hash, Functor, Traverse, and 29 more
kindlings-circe-derivation circe-generic-extras Encoder, Encoder.AsObject, Decoder
kindlings-fast-show-pretty (original) FastShowPretty
kindlings-jsoniter-derivation jsoniter-scala JsonCodecMaker JsonValueCodec, JsonCodec, JsonKeyCodec
kindlings-pureconfig-derivation PureConfig generic (JVM only) ConfigReader, ConfigWriter, ConfigConvert
kindlings-scalacheck-derivation manual instances Arbitrary, Cogen, Shrink
kindlings-sconfig-derivation (original) ConfigReader, ConfigWriter, ConfigCodec
kindlings-tapir-schema-derivation Tapir Schema.derived Schema
kindlings-ubjson-derivation (original) UBJsonValueCodec
kindlings-xml-derivation (original) XmlEncoder, XmlDecoder
kindlings-yaml-derivation scala-yaml derives YamlEncoder, YamlDecoder

All modules are cross-compiled for Scala 2.13 and 3, on JVM, Scala.js, and Scala Native — except kindlings-avro-derivation and kindlings-pureconfig-derivation, which are JVM-only.

Integrations

Module Description
kindlings-cats-integration NonEmptyList, NonEmptyVector, NonEmptyChain, Chain, NonEmptyMap, NonEmptySet, Validated, Const — handled automatically in all derivation modules
kindlings-iron-integration Iron constrained types (A :| C) — validated on decode, unwrapped on encode (Scala 3 only)
kindlings-refined-integration Refined types (Refined[A, P]) — validated on decode, unwrapped on encode

Add the integration jar to your build and the types work transparently — no imports, no configuration. The macro extension system discovers providers at compile time via SPI.

Extra

Module Description
kindlings-jsoniter-json Minimal JSON AST with optics and JsonValueCodec for jsoniter-scala — no Circe/Cats dependencies