SkillAgentSearch skills...

Kittens

Automatic type class derivation for Cats

Install / Use

/learn @typelevel/Kittens
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Kittens: automatic type class derivation for Cats

Kittens is a Scala library which provides instances of type classes from the [Cats][cats] library for arbitrary algebraic data types (ADTs) using [shapeless][shapeless]-based automatic type class derivation. It also provides utility functions related to Applicative such as lift, traverse and sequence to HList, Record and case classes.

kittens image

Kittens is part of the [Typelevel][typelevel] family of projects. It is an Open Source project under the Apache License v2, hosted on [GitHub][source]. Binary artifacts will be published to the [Sonatype OSS Repository Hosting service][sonatype] and synced to Maven Central.

It is available for Scala 2.12 and 2.13, Scala.js 1.5 and Scala Native 0.4.

To get started with sbt, add the following to your build.sbt file:

libraryDependencies += "org.typelevel" %% "kittens" % "latestVersion" // indicated in the badge below

Typelevel library Build status Gitter channel Scala.js Latest version

Scala 2

Instance derivations are available for the following type classes:

  • Eq, PartialOrder, Order, Hash
  • Show, pretty Show
  • Empty, EmptyK (from Alleycats)
  • Semigroup, CommutativeSemigroup, SemigroupK
  • Monoid, CommutativeMonoid, MonoidK
  • Functor, Contravariant, Invariant
  • Pure (from Alleycats), Apply, Applicative
  • Foldable, Reducible
  • Traverse, NonEmptyTraverse
  • ConsK (from Alleycats)

See the Type class support matrix for more details.

Derivation examples

scala> import cats.implicits._, cats._, cats.derived._

scala> case class Cat[Food](food: Food, foods: List[Food])
defined class Cat

scala> val cat = Cat(1, List(2, 3))
cat: Cat[Int] = Cat(1,List(2, 3))

Derive Functor

scala> implicit val fc: Functor[Cat] = semiauto.functor
FC: cats.Functor[Cat] = cats.derived.MkFunctor2$$anon$4@1c60573f

scala> cat.map(_ + 1)
res0: Cat[Int] = Cat(2,List(3, 4))

Derive Show

Note that the derived Show also prints out field names, so it might be preferable to the default toString:

scala> case class Address(street: String, city: String, state: String)
scala> case class ContactInfo(phoneNumber: String, address: Address)
scala> case class People(name: String, contactInfo: ContactInfo)

scala> val mike = People("Mike", ContactInfo("202-295-3928", Address("1 Main ST", "Chicago", "IL")))

scala> // existing Show instance for Address
scala> implicit val addressShow: Show[Address] =
         a => s"${a.street}, ${a.city}, ${a.state}"

scala> implicit val peopleShow: Show[People] = semiauto.show // auto derive Show for People

scala> mike.show
res0: String = People(name = Mike, contactInfo = ContactInfo(phoneNumber = 202-295-3928, address = 1 Main ST, Chicago, IL))

Note that in this example, the derivation generated instances for all referenced classes but still respected the existing instance in scope. For different ways to derive instances, please see Derivation on Scala 2 below.

Sequence examples

Note that to run these examples, you need partial unification enabled. For Scala 2.12 you should add the following to your build.sbt:

scalacOptions += "-Ypartial-unification"
scala> import cats.implicits._, cats.sequence._
import cats.implicits._
import cats.sequence._

scala> val f1 = (_: String).length
f1: String => Int = <function1>

scala> val f2 = (_: String).reverse
f2: String => String = <function1>

scala> val f3 = (_: String).toFloat
f3: String => Double = <function1>

scala> val f = sequence(f1, f2, f3)
f: String => shapeless.::[Int,shapeless.::[String,shapeless.::[Float,shapeless.HNil]]] = <function1>

scala> f("42.0")
res0: shapeless.::[Int,shapeless.::[String,shapeless.::[Float,shapeless.HNil]]] = 4 :: 0.24 :: 42.0 :: HNil

//or generic over ADTs
scala>  case class MyCase(a: Int, b: String, c: Float)
defined class MyCase

scala>  val myGen = sequenceGeneric[MyCase]
myGen: cats.sequence.sequenceGen[MyCase] = cats.sequence.SequenceOps$sequenceGen@63ae3243

scala> val f = myGen(a = f1, b = f2, c = f3)
f: String => MyCase = <function1>

scala> f("42.0")
res1: MyCase = MyCase(4,0.24,42.0)

Traverse works similarly except you need a shapeless.Poly.

Lift examples

scala> import cats._, implicits._, lift._
import cats._
import implicits._
import lift._

scala> def foo(x: Int, y: String, z: Float) = s"$x - $y - $z"

scala> val lifted = Applicative[Option].liftA(foo _)
lifted: (Option[Int], Option[String], Option[Float]) => Option[String] = <function3>

scala> lifted(Some(1), Some("a"), Some(3.2f))
res0: Option[String] = Some(1 - a - 3.2)

Derivation on Scala 2

There are three options for type class derivation on Scala 2: cats.derived.auto, cats.derived.cached and cats.derived.semiauto. The recommended best practice is to use semiauto:

import cats.derived

implicit val showFoo: Show[Foo] = derived.semiauto.show

This will respect all existing instances even if the field is a type constructor. For example Show[List[A]] will use the native Show instance for List and derived instance for A. And it manually caches the result to val showFoo.

Auto derivation

import derived.auto.show._

A downside is that it will derive an instance from scratch for every use site, increasing compilation time.

Cached derivation

import derived.cached.show._

Use this one with caution - it caches the derived instance globally. So it's only applicable if the instance is global in the application. This could be problematic for libraries, which have no control over the uniqueness of an instance at use site.

Semiauto derivation (recommended)

implicit val showFoo: Show[Foo] = derived.semiauto.show

A downside is we need to write one for every type that needs an instance.

Scala 3

There are five options for type class derivation on Scala 3. The recommended way is to import cats.derived.* and use derives clauses.

In contrast to Scala 2:

  • Cached derivation is not supported (and also not necessary)
  • Type Class Derivation is supported
  • A strict mode is available for semiauto and derives clauses

derives clause (recommended)

Kittens supports Scala 3's derivation syntax. Similar to Scala 2, instances will be derived recursively if necessary.

import cats.derived.*

// No instances declared for Name
case class Name(value: String)
case class Person(name: Name, age: Int) derives Eq, Show

enum CList[+A] derives Functor:
  case CNil
  case CCons(head: A, tail: CList[A])

Note that the derives clause has a fundamental limitation: it generates an instance that requires the type class for all type parameters, even if not necessary. The following example shows a rough equivalent of how a derives Monoid clause is desugared:

case class Concat[+A](left: Vector[A], right: Vector[A])
object Concat:
  // Note that the `Monoid[A]` requirement is not needed,
  // because `Monoid[Vector[A]]` is defined for any `A`.
  given [A: Monoid]: Monoid[Concat[A]] = Monoid.derived

In such cases it is recommended to use semiauto derivation, described below.

Semiauto derivation

This looks similar to semiauto for Scala 2. Instances will be derived recursively if necessary.

import cats.derived.semiauto

// No instances declared for Name
case class Name(value: String)
case class Person(name: Name, age: Int)

object Person:
  given Eq[Person] = semiauto.eq
  given Show[Person] = semiauto.show

enum CList[+A]:
  case CNil
  case CCons(head: A, tail: CList[A])
  
object CList:
  given Functor[CList] = semiauto.functor

Strict derives clause

Similar to derives above, but instances are not derived recursively (except for enums and sealed traits). Users need to be more explicit about which types implement an instance.

import cats.derived.strict.*

// The instances for Name need to be declared explicitly
case class Name(value: String) derives Eq, Show
case class Person(name: Name, age: Int) derives Eq, Show

// A coproduct type (enum) needs only a top-level declaration
enum CList[+A] derives Functor:
  case CNil
  case CCons(head: A, tail: CList[A])

The same limitations apply as with the default derives clause.

Strict semiauto derivation

Similar to semiauto above, but instances are not derived recursively (except for enums and sealed traits). Users need to be more explicit about which types implement an instance.

import cats.derived.strict

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

object Person:
  // The instances for Name need to be declared explicitly
  given Eq[Name] = strict.semiauto.eq
  given Show[Name] = strict.semiauto.show
  given Eq[Person] = strict.semiauto.eq
  given Show[Person] = strict.semiauto.show

enum CList[+A]:
  case CNil
  case CCons(head: A, tail: CList[A])
  
object CList:
  // A coproduct type (enum) needs only a top-level declaration
  given Functor[CList] = semiauto.functor

Auto derivation

This looks similar t

Related Skills

View on GitHub
GitHub Stars546
CategoryDevelopment
Updated2d ago
Forks66

Languages

Scala

Security Score

95/100

Audited on Mar 24, 2026

No findings