Kittens is a Scala library which provides instances of type classes from the Cats library for arbitrary algebraic data types (ADTs) using 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 is part of the Typelevel family of projects. It is an Open Source project under the Apache License v2, hosted on GitHub. Binary artifacts will be published to the Sonatype OSS Repository Hosting service 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
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.
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))
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))
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.
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
.
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)
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
.
import derived.auto.show._
A downside is that it will derive an instance from scratch for every use site, increasing compilation time.
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.
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:
strict
mode is available for semiauto
and derives
clausesderives
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.
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
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.
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
This looks similar to auto
for Scala 2.
import cats.derived.auto.eq.given import cats.derived.auto.show.given import cats.derived.auto.functor.given case class Name(value: String) case class Person(name: Name, age: Int) enum CList[+A]: case CNil case CCons(head: A, tail: CList[A])
We are currently unable to derive instances for nested type constructors, such as Functor[[x] =>> List[Set[x]]]
.
Our derived instances are not stack-safe. This is a departure from the behaviour for Scala 2 because we didn't want to incur the performance penalty of trampolining all instances in cats.Eval
. If your data-type is recursive or extremely large, then you may want to write instances by hand instead.
Kittens for Scala 3 is built on top of Shapeless 3 which has a completely different API than Shapeless 2, so we don't support features like Sequence
and Lift
.
ConsK
derivation is also not supported, although we expect this to be added in a future release.
Legend:
∀
- all must satisfy a constraint∃
- at least one must satisfy a constraint∃!
- exactly one must satisfy a constraint∧
- both constraints must be satisfied∨
- either constraint must be satisfiedλ[x => T]
Nested Types λ[x => F[G[x]]]
Applicative ∀ fields: Applicative ✗ for T: Monoid for F: Applicative and G: Applicative Apply ∀ fields: Apply ✗ for T: Semigroup for F: Apply and G: Apply Contravariant ∀ fields: Contravariant ∀ variants: Contravariant for any T for F: Functor and G: Contravariant EmptyK ∀ fields: EmptyK ∃! variant: EmptyK for T: Empty for F: EmptyK and any G ∨ for F: Pure and G: EmptyK Foldable ∀ fields: Foldable ∀ variants: Foldable for any T for F: Foldable and G: Foldable Functor ∀ fields: Functor ∀ variants: Functor for any T for F: Functor and G: Functor ∨ for F: Contravariant and G: Contravariant Invariant ∀ fields: Invariant ∀ variants: Invariant for any T for F: Invariant and G: Invariant MonoidK ∀ fields: MonoidK ✗ for T: Monoid for F: MonoidK and any G ∨ for F: Applicative and G: MonoidK NonEmptyTraverse ∃ field: NonEmptyTraverse ∧ ∀ fields: Traverse ∀ variants: NonEmptyTraverse ✗ for F: NonEmptyTraverse and G: NonEmptyTraverse Pure ∀ fields: Pure ✗ for T: Empty for F: Pure and G: Pure Reducible ∃ field: Reducible ∧ ∀ fields: Foldable ∀ variants: Reducible ✗ for F: Reducible and G: Reducible SemigroupK ∀ fields: SemigroupK ✗ for T: Semigroup for F: SemigroupK and any G ∨ for F: Apply and G: SemigroupK Traverse ∀ fields: Traverse ∀ variants: Traverse for any T for F: Traverse and G: Traverse Scala 3 only ↓ NonEmptyAlternative ∀ fields: NonEmptyAlternative ✗ ✗ for F: NonEmptyAlternative and G: Applicative Alternative ∀ fields: Alternative ✗ ✗ for F: Alternative and G: Applicative
The Kittens project supports the Scala code of conduct and wants all of its channels (mailing list, Gitter, GitHub, etc.) to be welcoming environments for everyone.
Kittens is built with SBT 1.x, and its master branch is built with Scala 2.13 by default.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4