Scalaz-core provides essential functional programming abstractions for Scala including type classes, data structures, and monad transformers.
—
Monad transformers allow composition of different monadic effects, enabling complex computations that combine multiple computational contexts.
Stateful computations that can modify state while computing values.
case class StateT[S, F[_], A](runF: S => F[(S, A)]) {
/** Run the stateful computation */
def run(initial: S)(implicit F: Monad[F]): F[(S, A)]
/** Execute and return only the final state */
def exec(initial: S)(implicit F: Monad[F]): F[S]
/** Evaluate and return only the result */
def eval(initial: S)(implicit F: Monad[F]): F[A]
/** Map over the result value */
def map[B](f: A => B)(implicit F: Functor[F]): StateT[S, F, B]
/** FlatMap for sequencing stateful computations */
def flatMap[B](f: A => StateT[S, F, B])(implicit F: Monad[F]): StateT[S, F, B]
/** Transform the state type */
def mapState[S2](f: S => S2, g: (S2, S) => S2)(implicit F: Functor[F]): StateT[S2, F, A]
/** Transform the inner monad */
def mapT[G[_]](f: F[(S, A)] => G[(S, A)]): StateT[S, G, A]
/** Lift StateT into another monad transformer */
def liftM[G[_[_], _]](implicit G: MonadTrans[G], F: Monad[F]): G[StateT[S, F, ?], A]
}
type State[S, A] = StateT[S, Id, A]
object StateT {
/** Lift a value into StateT */
def point[S, F[_], A](a: A)(implicit F: Applicative[F]): StateT[S, F, A]
/** Lift an F[A] into StateT */
def lift[S, F[_], A](fa: F[A])(implicit F: Functor[F]): StateT[S, F, A]
/** Get the current state */
def get[S, F[_]](implicit F: Applicative[F]): StateT[S, F, S]
/** Set the state */
def put[S, F[_]](s: S)(implicit F: Applicative[F]): StateT[S, F, Unit]
/** Modify the state */
def modify[S, F[_]](f: S => S)(implicit F: Applicative[F]): StateT[S, F, Unit]
/** Inspect the state with a function */
def gets[S, F[_], A](f: S => A)(implicit F: Applicative[F]): StateT[S, F, A]
}
object State {
/** Create a State computation */
def apply[S, A](f: S => (S, A)): State[S, A] = StateT[S, Id, A](f)
/** Get the current state */
def get[S]: State[S, S] = StateT.get[S, Id]
/** Set the state */
def put[S](s: S): State[S, Unit] = StateT.put[S, Id](s)
/** Modify the state */
def modify[S](f: S => S): State[S, Unit] = StateT.modify[S, Id](f)
/** Get part of the state */
def gets[S, A](f: S => A): State[S, A] = StateT.gets[S, Id, A](f)
}Usage Examples:
import scalaz._
import Scalaz._
// Simple counter state
val increment: State[Int, Int] = for {
current <- State.get[Int]
_ <- State.put(current + 1)
result <- State.get[Int]
} yield result
val (finalState, result) = increment.run(0) // (1, 1)
// Stack operations
def push[A](a: A): State[List[A], Unit] = State.modify(a :: _)
def pop[A]: State[List[A], Option[A]] = for {
stack <- State.get[List[A]]
_ <- State.put(stack.drop(1))
} yield stack.headOption
val stackOps = for {
_ <- push(1)
_ <- push(2)
top <- pop[Int]
} yield top
val (finalStack, result) = stackOps.run(List.empty[Int])Computations that read from a shared environment.
case class Kleisli[M[_], -A, +B](run: A => M[B]) {
/** Apply the Kleisli arrow */
def apply(a: A): M[B] = run(a)
/** Map over the result */
def map[C](f: B => C)(implicit M: Functor[M]): Kleisli[M, A, C]
/** FlatMap for sequencing Reader computations */
def flatMap[C](f: B => Kleisli[M, A, C])(implicit M: Monad[M]): Kleisli[M, A, C]
/** Compose with another Kleisli */
def compose[C](k: Kleisli[M, C, A])(implicit M: Monad[M]): Kleisli[M, C, B]
/** AndThen composition */
def andThen[C](k: Kleisli[M, B, C])(implicit M: Monad[M]): Kleisli[M, A, C]
/** Transform the input type */
def local[AA](f: AA => A): Kleisli[M, AA, B]
/** Transform the inner monad */
def mapT[N[_]](f: M[B] => N[B]): Kleisli[N, A, B]
/** Lower to function if M is Id */
def lower(implicit M: M[B] <:< B): A => B
}
type ReaderT[E, F[_], A] = Kleisli[F, E, A]
type Reader[E, A] = ReaderT[E, Id, A]
object Kleisli {
/** Lift a value into Kleisli */
def point[M[_], A, B](b: B)(implicit M: Applicative[M]): Kleisli[M, A, B]
/** Lift an M[B] into Kleisli */
def lift[M[_], A, B](mb: M[B]): Kleisli[M, A, B]
/** Ask for the environment */
def ask[M[_], A](implicit M: Applicative[M]): Kleisli[M, A, A]
/** Apply a function to the environment */
def asks[M[_], A, B](f: A => B)(implicit M: Applicative[M]): Kleisli[M, A, B]
}
object Reader {
/** Create a Reader */
def apply[E, A](f: E => A): Reader[E, A] = Kleisli[Id, E, A](f)
/** Ask for the environment */
def ask[E]: Reader[E, E] = Kleisli.ask[Id, E]
/** Apply function to environment */
def asks[E, A](f: E => A): Reader[E, A] = Kleisli.asks[Id, E, A](f)
}Usage Examples:
import scalaz._
import Scalaz._
case class Config(host: String, port: Int, database: String)
// Database operations that need config
def connectDb: Reader[Config, String] = Reader { config =>
s"Connected to ${config.database} at ${config.host}:${config.port}"
}
def queryUser(id: Int): Reader[Config, String] = for {
connection <- connectDb
config <- Reader.ask[Config]
} yield s"$connection - Queried user $id"
val config = Config("localhost", 5432, "myapp")
val result = queryUser(123).run(config)Computations that accumulate log values alongside results.
case class WriterT[W, F[_], A](run: F[(W, A)]) {
/** Map over the result value */
def map[B](f: A => B)(implicit F: Functor[F]): WriterT[W, F, B]
/** FlatMap for sequencing Writer computations */
def flatMap[B](f: A => WriterT[W, F, B])(implicit F: Monad[F], W: Semigroup[W]): WriterT[W, F, B]
/** Get only the log */
def written(implicit F: Functor[F]): F[W]
/** Get only the value */
def value(implicit F: Functor[F]): F[A]
/** Transform the log */
def mapLog[W2](f: W => W2)(implicit F: Functor[F]): WriterT[W2, F, A]
/** Transform both log and value */
def bimap[W2, B](f: W => W2, g: A => B)(implicit F: Functor[F]): WriterT[W2, F, B]
/** Clear the log */
def reset(implicit F: Functor[F], W: Monoid[W]): WriterT[W, F, A]
/** Swap log and value */
def swap(implicit F: Functor[F]): WriterT[A, F, W]
}
type Writer[W, A] = WriterT[W, Id, A]
object WriterT {
/** Create WriterT with log and value */
def apply[W, F[_], A](log: W, value: A)(implicit F: Applicative[F]): WriterT[W, F, A]
/** Lift a value into WriterT */
def point[W, F[_], A](value: A)(implicit W: Monoid[W], F: Applicative[F]): WriterT[W, F, A]
/** Lift an F[A] into WriterT */
def lift[W, F[_], A](fa: F[A])(implicit W: Monoid[W], F: Functor[F]): WriterT[W, F, A]
/** Create with only log */
def tell[W, F[_]](log: W)(implicit F: Applicative[F]): WriterT[W, F, Unit]
/** Create from F[(W, A)] */
def writerT[W, F[_], A](fwa: F[(W, A)]): WriterT[W, F, A]
}
object Writer {
/** Create Writer with log and value */
def apply[W, A](log: W, value: A): Writer[W, A] = WriterT[W, Id, A]((log, value))
/** Create with only value */
def value[W: Monoid, A](value: A): Writer[W, A] = Writer(Monoid[W].zero, value)
/** Create with only log */
def tell[W](log: W): Writer[W, Unit] = Writer(log, ())
}Usage Examples:
import scalaz._
import Scalaz._
// Computation with logging
def addWithLog(x: Int, y: Int): Writer[String, Int] =
Writer(s"Added $x and $y; ", x + y)
def multiplyWithLog(x: Int, y: Int): Writer[String, Int] =
Writer(s"Multiplied $x and $y; ", x * y)
val computation = for {
sum <- addWithLog(3, 4)
product <- multiplyWithLog(sum, 2)
} yield product
val (log, result) = computation.run // ("Added 3 and 4; Multiplied 7 and 2; ", 14)Computations that may fail, combining Option with another monad.
case class OptionT[F[_], A](run: F[Option[A]]) {
/** Map over the contained value */
def map[B](f: A => B)(implicit F: Functor[F]): OptionT[F, B]
/** FlatMap for sequencing optional computations */
def flatMap[B](f: A => OptionT[F, B])(implicit F: Monad[F]): OptionT[F, B]
/** Get the value or a default */
def getOrElse[AA >: A](default: => AA)(implicit F: Functor[F]): F[AA]
/** Get the value or compute alternative */
def orElse(alternative: => OptionT[F, A])(implicit F: Monad[F]): OptionT[F, A]
/** Check if the value exists */
def isDefined(implicit F: Functor[F]): F[Boolean]
/** Check if the value is empty */
def isEmpty(implicit F: Functor[F]): F[Boolean]
/** Filter with predicate */
def filter(p: A => Boolean)(implicit F: Functor[F]): OptionT[F, A]
/** Fold with functions for both cases */
def fold[B](ifEmpty: => B)(f: A => B)(implicit F: Functor[F]): F[B]
/** Transform the inner monad */
def mapT[G[_]](f: F[Option[A]] => G[Option[A]]): OptionT[G, A]
}
object OptionT {
/** Create OptionT with Some value */
def some[F[_], A](a: A)(implicit F: Applicative[F]): OptionT[F, A]
/** Create OptionT with None */
def none[F[_], A](implicit F: Applicative[F]): OptionT[F, A]
/** Lift F[A] into OptionT */
def liftF[F[_], A](fa: F[A])(implicit F: Functor[F]): OptionT[F, A]
/** Create from F[Option[A]] */
def optionT[F[_], A](foa: F[Option[A]]): OptionT[F, A]
/** Create from Option[A] */
def fromOption[F[_], A](oa: Option[A])(implicit F: Applicative[F]): OptionT[F, A]
}Similar to OptionT but using Scalaz's Maybe type.
case class MaybeT[F[_], A](run: F[Maybe[A]]) {
/** Map over the contained value */
def map[B](f: A => B)(implicit F: Functor[F]): MaybeT[F, B]
/** FlatMap for sequencing maybe computations */
def flatMap[B](f: A => MaybeT[F, B])(implicit F: Monad[F]): MaybeT[F, B]
/** Get the value or a default */
def getOrElse[AA >: A](default: => AA)(implicit F: Functor[F]): F[AA]
/** Get the value or compute alternative */
def orElse(alternative: => MaybeT[F, A])(implicit F: Monad[F]): MaybeT[F, A]
/** Check if the value exists */
def isDefined(implicit F: Functor[F]): F[Boolean]
/** Check if the value is empty */
def isEmpty(implicit F: Functor[F]): F[Boolean]
}
object MaybeT {
/** Create MaybeT with Just value */
def just[F[_], A](a: A)(implicit F: Applicative[F]): MaybeT[F, A]
/** Create MaybeT with Empty */
def empty[F[_], A](implicit F: Applicative[F]): MaybeT[F, A]
/** Lift F[A] into MaybeT */
def liftF[F[_], A](fa: F[A])(implicit F: Functor[F]): MaybeT[F, A]
}Computations that may fail with error values, combining Either with another monad.
case class EitherT[E, F[_], A](run: F[E \/ A]) {
/** Map over the right value */
def map[B](f: A => B)(implicit F: Functor[F]): EitherT[E, F, B]
/** FlatMap for sequencing Either computations */
def flatMap[B](f: A => EitherT[E, F, B])(implicit F: Monad[F]): EitherT[E, F, B]
/** Map over the left (error) value */
def leftMap[EE](f: E => EE)(implicit F: Functor[F]): EitherT[EE, F, A]
/** Bimap over both sides */
def bimap[EE, B](f: E => EE, g: A => B)(implicit F: Functor[F]): EitherT[EE, F, B]
/** Fold both sides */
def fold[B](left: E => B, right: A => B)(implicit F: Functor[F]): F[B]
/** Get right value or default */
def getOrElse[AA >: A](default: => AA)(implicit F: Functor[F]): F[AA]
/** Recover from error */
def recover[AA >: A](pf: PartialFunction[E, AA])(implicit F: Functor[F]): EitherT[E, F, AA]
/** Check if this is a right value */
def isRight(implicit F: Functor[F]): F[Boolean]
/** Check if this is a left value */
def isLeft(implicit F: Functor[F]): F[Boolean]
/** Swap left and right */
def swap(implicit F: Functor[F]): EitherT[A, F, E]
}
object EitherT {
/** Create EitherT with right value */
def right[E, F[_], A](a: A)(implicit F: Applicative[F]): EitherT[E, F, A]
/** Create EitherT with left value */
def left[E, F[_], A](e: E)(implicit F: Applicative[F]): EitherT[E, F, A]
/** Lift F[A] into EitherT as right */
def liftF[E, F[_], A](fa: F[A])(implicit F: Functor[F]): EitherT[E, F, A]
/** Create from F[E \/ A] */
def eitherT[E, F[_], A](fea: F[E \/ A]): EitherT[E, F, A]
/** Create from E \/ A */
def fromDisjunction[E, F[_], A](ea: E \/ A)(implicit F: Applicative[F]): EitherT[E, F, A]
}Usage Examples:
import scalaz._
import Scalaz._
// Database operations that might fail
def findUser(id: Int): EitherT[String, Future, User] =
if (id > 0) EitherT.right(Future.successful(User(id, "John")))
else EitherT.left(Future.successful("Invalid user ID"))
def findProfile(user: User): EitherT[String, Future, Profile] =
EitherT.right(Future.successful(Profile(user.id, "Developer")))
val result = for {
user <- findUser(123)
profile <- findProfile(user)
} yield UserWithProfile(user, profile)
// result: EitherT[String, Future, UserWithProfile]Computations that produce multiple results, combining List with another monad.
case class ListT[M[_], A](run: M[List[A]]) {
/** Map over the elements */
def map[B](f: A => B)(implicit M: Functor[M]): ListT[M, B]
/** FlatMap for sequencing list computations */
def flatMap[B](f: A => ListT[M, B])(implicit M: Monad[M]): ListT[M, B]
/** Filter elements */
def filter(p: A => Boolean)(implicit M: Functor[M]): ListT[M, A]
/** Take first n elements */
def take(n: Int)(implicit M: Functor[M]): ListT[M, A]
/** Drop first n elements */
def drop(n: Int)(implicit M: Functor[M]): ListT[M, A]
/** Append another ListT */
def ++(other: ListT[M, A])(implicit M: Monad[M]): ListT[M, A]
/** Convert to List in the monad */
def toList: M[List[A]] = run
}
object ListT {
/** Create empty ListT */
def empty[M[_], A](implicit M: Applicative[M]): ListT[M, A]
/** Create ListT with single element */
def single[M[_], A](a: A)(implicit M: Applicative[M]): ListT[M, A]
/** Lift M[A] into ListT */
def liftF[M[_], A](ma: M[A])(implicit M: Functor[M]): ListT[M, A]
/** Create from M[List[A]] */
def listT[M[_], A](mla: M[List[A]]): ListT[M, A]
/** Create from List[A] */
def fromList[M[_], A](la: List[A])(implicit M: Applicative[M]): ListT[M, A]
}Combination of Reader, Writer, and State transformers.
case class ReaderWriterStateT[R, W, S, F[_], A](run: (R, S) => F[(W, A, S)]) {
/** Map over the result value */
def map[B](f: A => B)(implicit F: Functor[F]): ReaderWriterStateT[R, W, S, F, B]
/** FlatMap for sequencing computations */
def flatMap[B](f: A => ReaderWriterStateT[R, W, S, F, B])(implicit F: Monad[F], W: Semigroup[W]): ReaderWriterStateT[R, W, S, F, B]
/** Execute the computation */
def runRWST(r: R, s: S): F[(W, A, S)] = run(r, s)
/** Execute and return only the result */
def eval(r: R, s: S)(implicit F: Functor[F]): F[A]
/** Execute and return only the final state */
def exec(r: R, s: S)(implicit F: Functor[F]): F[S]
/** Execute and return only the log */
def logged(r: R, s: S)(implicit F: Functor[F]): F[W]
}
type ReaderWriterState[R, W, S, A] = ReaderWriterStateT[R, W, S, Id, A]
type RWST[R, W, S, F[_], A] = ReaderWriterStateT[R, W, S, F, A]
type RWS[R, W, S, A] = ReaderWriterState[R, W, S, A]
object ReaderWriterStateT {
/** Ask for the environment */
def ask[R, W, S, F[_]](implicit W: Monoid[W], F: Applicative[F]): ReaderWriterStateT[R, W, S, F, R]
/** Tell (log) a value */
def tell[R, W, S, F[_]](w: W)(implicit F: Applicative[F]): ReaderWriterStateT[R, W, S, F, Unit]
/** Get the current state */
def get[R, W, S, F[_]](implicit W: Monoid[W], F: Applicative[F]): ReaderWriterStateT[R, W, S, F, S]
/** Set the state */
def put[R, W, S, F[_]](s: S)(implicit W: Monoid[W], F: Applicative[F]): ReaderWriterStateT[R, W, S, F, Unit]
/** Modify the state */
def modify[R, W, S, F[_]](f: S => S)(implicit W: Monoid[W], F: Applicative[F]): ReaderWriterStateT[R, W, S, F, Unit]
}Usage Examples:
import scalaz._
import Scalaz._
case class Config(debug: Boolean)
type Log = List[String]
type Counter = Int
def increment: ReaderWriterState[Config, Log, Counter, Unit] = for {
config <- ReaderWriterStateT.ask[Config, Log, Counter, Id]
count <- ReaderWriterStateT.get[Config, Log, Counter, Id]
_ <- ReaderWriterStateT.put[Config, Log, Counter, Id](count + 1)
_ <- if (config.debug)
ReaderWriterStateT.tell[Config, Log, Counter, Id](List(s"Incremented to ${count + 1}"))
else
ReaderWriterStateT.point[Config, Log, Counter, Id, Unit](())
} yield ()
val config = Config(debug = true)
val (log, result, finalState) = increment.run(config, 0)Install with Tessl CLI
npx tessl i tessl/maven-org-scalaz--scalaz-core-sjs1-2-13