Cats-core provides essential abstractions and type classes for functional programming in Scala including Functor, Monad, Applicative, and many others.
npx @tessl/cli install tessl/maven-org-typelevel--cats-core_3@2.13.0Cats-core is the foundational functional programming library for Scala that provides essential abstractions and type classes for safe, composable, and expressive functional programming. It includes fundamental type classes like Functor, Monad, and Applicative, along with powerful data types and monad transformers.
Package: cats-core
Version: 2.13.0
Organization: org.typelevel
// SBT
libraryDependencies += "org.typelevel" %% "cats-core" % "2.13.0"
// Mill
ivy"org.typelevel::cats-core:2.13.0"
// Maven
<dependency>
<groupId>org.typelevel</groupId>
<artifactId>cats-core_3</artifactId>
<version>2.13.0</version>
</dependency>import cats.implicits._
// Imports all syntax extensions and type class instances
// Most convenient for general usage
// Example usage after import
val result = List(1, 2, 3).map(_ + 1) // List(2, 3, 4)
val combined = (Option(1), Option(2)).mapN(_ + _) // Some(3)// All syntax extensions only
import cats.syntax.all._
// All type class instances only
import cats.instances.all._
// Specific type classes
import cats.{Functor, Applicative, Monad}
import cats.syntax.functor._
import cats.syntax.applicative._
// Specific data types
import cats.data.{NonEmptyList, Validated, EitherT}
import cats.data.Validated.{Valid, Invalid}
// Kernel types (re-exported from cats.kernel)
import cats.{Eq, Order, Semigroup, Monoid} // Available directly from cats package
// Or import from kernel directly:
// import cats.kernel.{Eq, Order, Semigroup, Monoid}
// Instances for specific standard library types
import cats.instances.list._
import cats.instances.option._
import cats.instances.either._import cats.Functor
import cats.syntax.functor._
// Summon type class instances
val listFunctor = Functor[List]
val optionFunctor = Functor[Option]
// Use summoned instances
listFunctor.map(List(1, 2, 3))(_ * 2) // List(2, 4, 6)
// Or use syntax extensions directly
List(1, 2, 3).map(_ * 2) // List(2, 4, 6)import cats.syntax.functor._
// Transform values inside containers
List(1, 2, 3).map(_ + 1) // List(2, 3, 4)
Option(5).map(_ * 2) // Some(10)
Right(10).map(_.toString) // Right("10")
// Chain transformations
List("hello", "world")
.map(_.toUpperCase)
.map(_.length) // List(5, 5)import cats.syntax.applicative._
import cats.syntax.apply._
// Lift pure values
42.pure[Option] // Some(42)
"hello".pure[List] // List("hello")
// Combine multiple wrapped values
(Option(1), Option(2), Option(3)).mapN(_ + _ + _) // Some(6)
(List(1, 2), List(10, 20)).mapN(_ + _) // List(11, 21, 12, 22)
// Product of values
(Option("hello"), Option(42)).tupled // Some(("hello", 42))import cats.syntax.flatMap._
import cats.syntax.functor._
// Chain dependent computations
def divide(a: Int, b: Int): Option[Double] =
if (b != 0) Some(a.toDouble / b) else None
for {
x <- Some(10)
y <- Some(2)
result <- divide(x, y)
} yield result // Some(5.0)
// Equivalent with flatMap/map
Some(10).flatMap(x =>
Some(2).flatMap(y =>
divide(x, y))) // Some(5.0)import cats.data.Validated
import cats.data.Validated.{Valid, Invalid}
import cats.syntax.apply._
type ValidationResult[A] = Validated[List[String], A]
def validateName(name: String): ValidationResult[String] =
if (name.nonEmpty) Valid(name)
else Invalid(List("Name cannot be empty"))
def validateAge(age: Int): ValidationResult[Int] =
if (age >= 0) Valid(age)
else Invalid(List("Age must be positive"))
// Accumulate all validation errors
case class Person(name: String, age: Int)
val result = (validateName(""), validateAge(-5)).mapN(Person.apply)
// Invalid(List("Name cannot be empty", "Age must be positive"))Cats-core is organized around a hierarchy of type classes that build upon each other:
Functor[F[_]]
|
Apply[F[_]]
|
Applicative[F[_]]
|
FlatMap[F[_]]
|
Monad[F[_]]
|
ApplicativeError[F[_], E]
|
MonadError[F[_], E]cats - Fundamental type classes (Functor, Monad, etc.)cats.data - Data types and monad transformerscats.syntax - Extension methods for type classescats.instances - Type class instances for standard library typescats.arrow - Arrow type classes for function-like typesEssential abstractions for functional programming patterns.
import cats.{Functor, Apply, Applicative, FlatMap, Monad}
// Functor - map over wrapped values
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
// Apply - apply wrapped functions to wrapped values
trait Apply[F[_]] extends Functor[F] {
def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]
def map2[A, B, Z](fa: F[A], fb: F[B])(f: (A, B) => Z): F[Z]
}
// Applicative - lift pure values + Apply
trait Applicative[F[_]] extends Apply[F] {
def pure[A](x: A): F[A]
}
// FlatMap - monadic bind operation
trait FlatMap[F[_]] extends Apply[F] {
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
}
// Monad - combines Applicative + FlatMap
trait Monad[F[_]] extends Applicative[F] with FlatMap[F]See Core Type Classes for complete API documentation.
import cats.{ApplicativeError, MonadError}
// Error handling for Applicative
trait ApplicativeError[F[_], E] extends Applicative[F] {
def raiseError[A](e: E): F[A]
def handleErrorWith[A](fa: F[A])(f: E => F[A]): F[A]
def attempt[A](fa: F[A]): F[Either[E, A]]
}
// Error handling for Monad
trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] {
def ensure[A](fa: F[A])(error: => E)(predicate: A => Boolean): F[A]
}
// Common type aliases
type ApplicativeThrow[F[_]] = ApplicativeError[F, Throwable]
type MonadThrow[F[_]] = MonadError[F, Throwable]See Error Handling for patterns and examples.
import cats.{Foldable, Traverse}
// Fold structures to summary values
trait Foldable[F[_]] {
def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B]
def foldMap[A, B](fa: F[A])(f: A => B)(implicit B: Monoid[B]): B
}
// Traverse with effects
trait Traverse[F[_]] extends Foldable[F] with Functor[F] {
def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Applicative[G]): G[F[B]]
def sequence[G[_], A](fga: F[G[A]])(implicit G: Applicative[G]): G[F[A]]
}Essential data types for functional programming.
import cats.data.{NonEmptyList, NonEmptyChain}
// Lists guaranteed to have at least one element
val nel = NonEmptyList.of(1, 2, 3) // NonEmptyList[Int]
val single = NonEmptyList.one(42) // NonEmptyList[Int]
val fromList = NonEmptyList.fromList(List(1, 2)) // Option[NonEmptyList[Int]]
// Efficient append/prepend chains
val nec = NonEmptyChain.of("a", "b", "c") // NonEmptyChain[String]
val appended = nec.append("d") // O(1) operationimport cats.data.{Validated, Ior}
import cats.data.Validated.{Valid, Invalid}
// Error accumulation
val validation: Validated[String, Int] = Valid(42)
val error: Validated[String, Int] = Invalid("Error message")
// Inclusive OR - can have left, right, or both
val left = Ior.Left("warning") // Ior[String, Int]
val right = Ior.Right(42) // Ior[String, Int]
val both = Ior.Both("warning", 42) // Ior[String, Int]See Data Types for comprehensive coverage of all data types.
Stack multiple monadic effects together.
import cats.data.{OptionT, EitherT}
import cats.effect.IO
// Stack Option with IO
type AsyncOption[A] = OptionT[IO, A]
val computation: AsyncOption[String] = for {
user <- OptionT(getUserById(userId)) // IO[Option[User]]
profile <- OptionT(getProfile(user.id)) // IO[Option[Profile]]
} yield profile.displayName // AsyncOption[String]
// Stack Either with IO for error handling
type AsyncResult[A] = EitherT[IO, String, A]
val result: AsyncResult[User] = for {
input <- EitherT(validateInput(data)) // IO[Either[String, Input]]
user <- EitherT(createUser(input)) // IO[Either[String, User]]
} yield user // AsyncResult[User]import cats.data.{StateT, ReaderT}
// Stateful computations
type GameState[A] = StateT[IO, GameWorld, A]
val gameLoop: GameState[Unit] = for {
world <- StateT.get[IO, GameWorld] // Get current state
_ <- StateT.modify[IO, GameWorld](updatePhysics) // Modify state
_ <- StateT.liftF(renderFrame(world)) // Lift IO into StateT
} yield ()
// Dependency injection pattern
type App[A] = ReaderT[IO, AppConfig, A]
val application: App[String] = for {
config <- ReaderT.ask[IO, AppConfig] // Access environment
result <- ReaderT.liftF(processWithConfig(config)) // Lift IO
} yield resultSee Monad Transformers for detailed transformer usage.
Extension methods that make type class operations available as natural method calls.
import cats.syntax.functor._
import cats.syntax.applicative._
import cats.syntax.apply._
// Functor operations
List(1, 2, 3).map(_ * 2) // List(2, 4, 6)
List(1, 2, 3).as("x") // List("x", "x", "x")
List(1, 2, 3).void // List((), (), ())
// Applicative operations
42.pure[List] // List(42)
(List(1, 2), List(10, 20)).mapN(_ + _) // List(11, 21, 12, 22)
(List(1), List(2)).tupled // List((1, 2))
// Apply sequencing
List(1, 2) <* List("a", "b") // List(1, 1, 2, 2)
List(1, 2) *> List("a", "b") // List("a", "b", "a", "b")import cats.syntax.flatMap._
import cats.syntax.traverse._
// Monad operations
List(1, 2).flatMap(x => List(x, x * 10)) // List(1, 10, 2, 20)
List(1, 2) >> List("a", "b") // List("a", "b", "a", "b")
// Traverse operations
List(1, 2, 3).traverse(x => Option(x)) // Some(List(1, 2, 3))
List(Some(1), Some(2), Some(3)).sequence // Some(List(1, 2, 3))See Syntax Extensions for all available syntax.
Pre-built instances for Scala standard library types.
import cats.instances.list._
import cats.instances.vector._
import cats.instances.option._
// Lists have Monad, Traverse, Alternative
List(1, 2, 3).flatMap(x => List(x, -x)) // List(1, -1, 2, -2, 3, -3)
// Vectors have same instances as Lists
Vector(1, 2, 3).traverse(Option(_)) // Some(Vector(1, 2, 3))
// Option has Monad, Alternative, Traverse
Option(1).flatMap(x => if (x > 0) Some(x * 2) else None) // Some(2)import cats.instances.either._
import cats.instances.try_._
// Either has Monad in right projection
Right(10).flatMap(x => if (x > 5) Right(x * 2) else Left("too small")) // Right(20)
// Try has MonadError for exception handling
import scala.util.Try
Try(10 / 2).recover { case _: ArithmeticException => 0 } // Success(5)See Instances for complete instance documentation.
import cats.data.ValidatedNec
import cats.syntax.validated._
import cats.syntax.apply._
// Validated with NonEmptyChain for error accumulation
type ValidationResult[A] = ValidatedNec[String, A]
case class User(name: String, email: String, age: Int)
def validateName(name: String): ValidationResult[String] =
if (name.trim.nonEmpty) name.valid
else "Name cannot be empty".invalidNec
def validateEmail(email: String): ValidationResult[String] =
if (email.contains("@")) email.valid
else "Email must be valid".invalidNec
def validateAge(age: Int): ValidationResult[Int] =
if (age >= 18) age.valid
else "Must be 18 or older".invalidNec
// All errors collected together
val userValidation = (
validateName(""),
validateEmail("invalid"),
validateAge(16)
).mapN(User.apply)
// Invalid(Chain(Name cannot be empty, Email must be valid, Must be 18 or older))import cats.syntax.parallel._
// Process validations in parallel for better performance
val parallelValidation = (
validateName("John"),
validateEmail("john@example.com"),
validateAge(25)
).parMapN(User.apply)
// Valid(User("John", "john@example.com", 25))All Cats operations are stack-safe through careful implementation:
import cats.Eval
// Stack-safe recursive computation
def factorial(n: BigInt): Eval[BigInt] = {
if (n <= 1) Eval.now(BigInt(1))
else factorial(n - 1).map(_ * n)
}
val result = factorial(100000).value // Won't stack overflow
// Stack-safe traverse operations
val largeList = (1 to 100000).toList
val traversed = largeList.traverse(x => Eval.later(x * 2))
traversed.value // Safe computationThis introduction covers the fundamental concepts and common patterns. For detailed documentation on specific areas:
Each section provides comprehensive API documentation with practical examples for effective usage of cats-core in functional Scala programming.