Top Related Projects
Lightweight, modular, and extensible library for functional programming.
The Scala 3 compiler, also known as Dotty.
Generic programming for Scala
ZIO — A type-safe, composable library for async and concurrent programming in Scala
Powerful new number types and numeric abstractions for Scala.
Compositional, streaming I/O library for Scala
Quick Overview
Scalaz is a Scala library for functional programming. It provides a set of type classes and data structures that enhance Scala's capabilities, making it easier to write pure functional code. Scalaz aims to improve type safety and composability in Scala applications.
Pros
- Extensive set of functional programming abstractions and utilities
- Strong focus on type safety and correctness
- Encourages writing more composable and maintainable code
- Well-documented with a large community and ecosystem
Cons
- Steep learning curve for developers new to functional programming
- Can lead to complex type signatures that may be difficult to understand
- Some performance overhead compared to imperative code
- May conflict with other libraries or frameworks that have different design philosophies
Code Examples
Example 1: Using Monads
import scalaz._
import Scalaz._
val result = for {
a <- Some(5)
b <- Some(10)
} yield a + b
println(result) // Output: Some(15)
Example 2: Validating input using Validation
import scalaz._
import Scalaz._
case class User(name: String, age: Int)
def validateName(name: String): Validation[String, String] =
if (name.nonEmpty) name.success else "Name cannot be empty".failure
def validateAge(age: Int): Validation[String, Int] =
if (age >= 18) age.success else "Age must be at least 18".failure
def createUser(name: String, age: Int): Validation[String, User] =
(validateName(name) |@| validateAge(age))(User.apply)
println(createUser("Alice", 25)) // Output: Success(User(Alice,25))
println(createUser("", 16)) // Output: Failure(Name cannot be empty,Age must be at least 18)
Example 3: Using Lenses for immutable updates
import scalaz._
import Scalaz._
case class Address(street: String, city: String)
case class Person(name: String, address: Address)
val cityLens = Lens.lensu[Person, String](
(p, c) => p.copy(address = p.address.copy(city = c)),
_.address.city
)
val person = Person("Alice", Address("123 Main St", "New York"))
val updatedPerson = cityLens.set(person, "San Francisco")
println(updatedPerson) // Output: Person(Alice,Address(123 Main St,San Francisco))
Getting Started
To use Scalaz in your project, add the following dependency to your build.sbt
file:
libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.3.6"
Then, import Scalaz in your Scala files:
import scalaz._
import Scalaz._
You can now start using Scalaz's functional programming constructs in your code.
Competitor Comparisons
Lightweight, modular, and extensible library for functional programming.
Pros of Cats
- More beginner-friendly documentation and learning resources
- Broader community support and active development
- Better integration with other Typelevel ecosystem projects
Cons of Cats
- Slightly larger dependency size
- Some advanced features may be less performant than Scalaz equivalents
Code Comparison
Scalaz:
import scalaz._
import Scalaz._
val result = List(1, 2, 3).foldMap(identity)
Cats:
import cats._
import cats.implicits._
val result = List(1, 2, 3).foldMap(identity)
Both libraries provide similar functionality, but Cats often has more intuitive syntax and better type inference. Scalaz tends to use more symbolic operators, which can be concise but harder to read for beginners.
Scalaz has been around longer and may have more optimized implementations for certain operations, while Cats focuses on providing a more approachable and modular API.
Overall, both libraries are powerful tools for functional programming in Scala, with Cats gaining popularity due to its more accessible approach and active community support.
The Scala 3 compiler, also known as Dotty.
Pros of Scala3
- Official Scala language implementation with broader community support
- Introduces new features like enums, opaque types, and extension methods
- Improved type inference and compile times
Cons of Scala3
- Steeper learning curve for new language features
- Some breaking changes from Scala 2, requiring migration efforts
Code Comparison
Scala3:
enum Color:
case Red, Green, Blue
extension (s: String)
def capitalize = s.toUpperCase
Scalaz:
sealed trait Color
case object Red extends Color
case object Green extends Color
case object Blue extends Color
implicit class StringOps(val s: String) extends AnyVal {
def capitalize = s.toUpperCase
}
Key Differences
- Scala3 focuses on language evolution and modernization
- Scalaz emphasizes functional programming abstractions and patterns
- Scala3 has a larger scope, covering the entire language ecosystem
- Scalaz provides a library of functional programming tools for Scala
Community and Adoption
- Scala3 has wider adoption as the official Scala implementation
- Scalaz has a dedicated following in the functional programming community
Development Activity
- Scala3 has more frequent updates and contributions
- Scalaz development is more focused on maintaining existing functionality
Generic programming for Scala
Pros of Shapeless
- More focused on generic programming and type-level computation
- Easier learning curve for developers new to advanced Scala concepts
- Better integration with modern Scala libraries and frameworks
Cons of Shapeless
- Less comprehensive than Scalaz in terms of functional programming constructs
- Smaller community and ecosystem compared to Scalaz
- May require more boilerplate code for certain operations
Code Comparison
Shapeless example (HList manipulation):
import shapeless._
val hlist = 1 :: "hello" :: true :: HNil
val result = hlist.head // 1
Scalaz example (Functor usage):
import scalaz._
import Scalaz._
val list = List(1, 2, 3)
val result = list.map(_ + 1) // List(2, 3, 4)
Both libraries offer powerful tools for Scala developers, but they serve different purposes. Shapeless excels in generic programming and type-level computation, while Scalaz provides a more comprehensive set of functional programming abstractions. The choice between them depends on the specific needs of your project and your team's familiarity with advanced Scala concepts.
ZIO — A type-safe, composable library for async and concurrent programming in Scala
Pros of ZIO
- More beginner-friendly with better documentation and learning resources
- Focuses on practical, real-world use cases and performance optimization
- Offers a broader ecosystem of libraries and integrations
Cons of ZIO
- Relatively newer project with less historical stability
- May have a steeper learning curve for developers already familiar with Scalaz
Code Comparison
Scalaz:
import scalaz._
import Scalaz._
val result = for {
a <- Some(5)
b <- Some(10)
} yield a + b
ZIO:
import zio._
val program = for {
a <- ZIO.succeed(5)
b <- ZIO.succeed(10)
} yield a + b
val result = Runtime.default.unsafeRun(program)
Summary
ZIO and Scalaz are both functional programming libraries for Scala, but they have different focuses. ZIO is more modern and practical, emphasizing real-world applications and performance. It offers a rich ecosystem and better documentation, making it more accessible to newcomers. However, Scalaz has a longer history and may be preferred by developers already familiar with its concepts. The code comparison shows that while both libraries support similar functional programming patterns, ZIO's approach is more explicitly focused on effects and runtime management.
Powerful new number types and numeric abstractions for Scala.
Pros of Spire
- More focused on numeric types and operations, providing specialized implementations for performance
- Easier learning curve and more approachable for developers new to functional programming
- Active development and regular updates
Cons of Spire
- Less comprehensive in terms of general-purpose functional programming constructs
- Smaller ecosystem and fewer third-party libraries compared to Scalaz
Code Comparison
Scalaz example (Option monadic operations):
import scalaz._
import Scalaz._
val result = for {
a <- Some(5)
b <- Some(10)
} yield a + b
Spire example (Numeric operations):
import spire.math._
import spire.implicits._
val a = Complex(1, 2)
val b = Complex(3, 4)
val result = a * b
Scalaz focuses on general functional programming constructs, while Spire specializes in numeric operations and performance-oriented implementations. Scalaz provides a wider range of abstractions and type classes, making it more suitable for complex functional programming patterns. Spire, on the other hand, excels in numeric computations and offers better performance for mathematical operations.
Scalaz has a steeper learning curve but provides a more comprehensive set of tools for functional programming. Spire is more accessible and focused, making it a good choice for projects that primarily deal with numeric computations and don't require the full breadth of functional programming abstractions.
Compositional, streaming I/O library for Scala
Pros of fs2
- More focused on streaming and I/O operations
- Generally considered more beginner-friendly
- Active development and community support
Cons of fs2
- Less comprehensive in terms of functional programming abstractions
- Smaller ecosystem compared to Scalaz
Code Comparison
fs2 example:
import cats.effect.IO
import fs2.Stream
val stream: Stream[IO, Int] = Stream(1, 2, 3).covary[IO]
val result: IO[List[Int]] = stream.compile.toList
Scalaz example:
import scalaz._, Scalaz._
val list = List(1, 2, 3)
val result = list.foldMap(identity)
Key Differences
- fs2 focuses on streaming operations, while Scalaz provides a broader set of functional programming tools
- fs2 integrates well with Cats Effect for handling side effects, whereas Scalaz has its own effect system
- Scalaz offers more advanced type-level programming features compared to fs2
Use Cases
- Choose fs2 for stream processing and I/O-heavy applications
- Opt for Scalaz when working on projects requiring advanced functional programming concepts and abstractions
Community and Ecosystem
- fs2 is part of the Typelevel ecosystem, which promotes approachable functional programming
- Scalaz has a longer history and a more established ecosystem, but with a steeper learning curve
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Scalaz
Scalaz is a Scala library for functional programming.
It provides purely functional data structures to complement those from the Scala standard library.
It defines a set of foundational type classes (e.g. Functor
, Monad
) and corresponding instances for
a large number of data structures.
Getting Scalaz
The current stable version is 7.3.8, which is cross-built against Scala 2.12.x, 2.13.x, 3.x and Scala.js, scala-native.
If you're using SBT, add the following line to your build file:
libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.3.8"
For Maven and other build tools, you can visit search.maven.org. (This search will also list all available modules of scalaz.)
To get sample configurations, click on the version of the module you are interested in.
You can also find direct download links at the bottom of that page. Choose the file ending in 7.3.8.jar
.
Quick Start
import scalaz._
import std.option._, std.list._ // functions and type class instances for Option and List
scala> Apply[Option].apply2(some(1), some(2))((a, b) => a + b)
res0: Option[Int] = Some(3)
scala> Traverse[List].traverse(List(1, 2, 3))(i => some(i))
res1: Option[List[Int]] = Some(List(1, 2, 3))
Use of the Ops
classes, defined under scalaz.syntax
.
import scalaz._
import std.list._ // type class instances for List
import syntax.bind._ // syntax for the Bind type class (and its parents)
scala> List(List(1)).join
res0: List[Int] = List(1)
scala> List(true, false).ifM(List(0, 1), List(2, 3))
res1: List[Int] = List(0, 1, 2, 3)
We've gone to great lengths to give you an a-la-carte importing experience, but if you prefer an all-you-can-eat buffet, you're in luck:
import scalaz._
import Scalaz._
scala> NonEmptyList(1, 2, 3).cojoin
res0: scalaz.NonEmptyList[scalaz.NonEmptyList[Int]] = NonEmptyList(NonEmptyList(1, 2, 3), NonEmptyList(2, 3), NonEmptyList(3))
scala> 1.node(2.leaf, 3.node(4.leaf))
res1: scalaz.Tree[Int] = <tree>
scala> List(some(1), none).suml
res2: Option[Int] = Some(1)
Resources
Let the types speak for themselves via the Scalaz Scaladocs!
The examples module contains some snippets of Scalaz usage.
The wiki contains release and migration information.
Talk with us by joining IRC: irc.libera.chat channel #scalaz, or join the Scalaz mailing list on Google Groups.
The typelevel blog has some great posts such as Towards Scalaz by Adelbert Chang.
Learning Scalaz is a great series of blog posts by Eugene Yokota. Thanks, Eugene!
Changes in Version 7
Scalaz 7 represents a major reorganization of the library. We have taken a fresh look at the challenges of encoding type classes in Scala, in particular at when and how to employ the implicit scope.
At a glance
scalaz.{effect, iteratee}
split to separate sub-projects;scalaz.{http, geo}
dropped.- Refined and expanded the type class hierarchy.
- Type class instances are no longer defined in the companion objects of the type class.
Instances for standard library types are defined under
scalaz.std
, and instances for Scalaz data types are defined in the companion object for those types. An instance definition can provide multiple type classes in a single place, which was not always possible in Scalaz 6. - Type class instances have been organized to avoid ambiguity, a problem that arises when
instances are dependent on other instances (for example,
Monoid[(A, B)]
) - Use of implicit views to provide access to Scalaz functionality as extension methods
has been segregated to
scalaz.syntax
, and can be imported selectively, and need not be used at all. - Related functions are defined in the type class trait, to support standalone
usage of the type class. In Scalaz 6, these were defined in
Identity
,MA
, orMAB
. - New data structures have been added, and existing ones generalized. A number of monad transformers have been provided, in some cases generalizing old data structures.
Modularity
Scalaz has been modularised.
- scalaz-core: Type class hierarchy, data structures, type class instances for the Scala and Java standard libraries, implicit conversions / syntax to access these.
- scalaz-effect: Data structures to represent and compose IO effects in the type system.
- scalaz-iteratee: Experimental new Iteratee implementation
Type Class Hierarchy
- Type classes form an inheritance hierarchy, as in Scalaz 6. This is convenient both at the call site and at the type class instance definition. At the call site, it ensures that you can call a method requiring a more general type class with an instance of a more specific type class:
def bar[M[_]: Functor] = ()
def foo[M[_]: Monad] = bar[M] // Monad[M] is a subtype of Functor[M]
- The hierarchy itself is largely the same as in Scalaz 6. However, there have been a few adjustments, some method signatures have been adjusted to support better standalone usage, so code depending on these will need to be re-worked.
Type Class Instance Definition
- Constructive implicits, which create a type class instance automatically based on instances of all parent type classes, are removed. These led to subtle errors with ambiguous implicits, such as this problem with FunctorBindApply
- Type class instances are no longer declared in fragments in the companion objects of the type class. Instead, they
are defined in the package
scalaz.std
, and must be imported. These instances are defined in traits which will be mixed together into an object for importing en-masse, if desired. - A single implicit can define a number of type class instances for a type.
- A type class definition can override methods (including derived methods) for efficiency.
Here is an instance definition for Option
. Notice that the method map
has been overridden.
implicit val option: Traverse[Option] with MonadPlus[Option] = new Traverse[Option] with MonadPlus[Option] {
def point[A](a: => A) = Some(a)
def bind[A, B](fa: Option[A])(f: A => Option[B]): Option[B] = fa flatMap f
override def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa map f
def traverseImpl[F[_], A, B](fa: Option[A])(f: A => F[B])(implicit F: Applicative[F]) =
fa map (a => F.map(f(a))(Some(_): Option[B])) getOrElse F.point(None)
def empty[A]: Option[A] = None
def plus[A](a: Option[A], b: => Option[A]) = a orElse b
def foldR[A, B](fa: Option[A], z: B)(f: (A) => (=> B) => B): B = fa match {
case Some(a) => f(a)(z)
case None => z
}
}
To use this, one would:
import scalaz.std.option.optionInstance
// or, importing all instances en-masse
// import scalaz.Scalaz._
val M = Monad[Option]
val oi: Option[Int] = M.point(0)
Syntax
We co-opt the term syntax to refer to the way we allow the functionality of Scalaz to be
called in the object.method(args)
form, which can be easier to read, and, given that type inference
in Scala flows from left-to-right, can require fewer type annotations.
- No more
Identity
,MA
, orMAB
from Scalaz 6. - Syntax is segregated from rest of the library, in a sub-package
scalaz.syntax
. - All Scalaz functionality is available without using the provided syntax, by directly calling methods on the type class or its companion object.
- Syntax is available a-la-carte. You can import the syntax for working with particular type classes where you need it. This avoids flooding the autocompletion in your IDE with every possible extension method. This should also help compiler performance, by reducing the implicit search space.
- Syntax is layered in the same way as type classes. Importing the syntax for, say,
Applicative
will also provide the syntax forApply
andFunctor
.
Syntax can be imported in two ways. Firstly, the syntax specialized for a particular instance of a type class can be imported directly from the instance itself.
// import the type class instance
import scalaz.std.option.optionInstance
// import the implicit conversions to `MonadOps[Option, A]`, `BindOps[Option, A]`, ...
import optionInstance.monadSyntax._
val oi: Option[Option[Int]] = Some(Some(1))
// Expands to: `ToBindOps(io).join`
oi.join
Alternatively, the syntax can be imported for a particular type class.
// import the type class instance
import scalaz.std.option.optionInstance
// import the implicit conversions to `MonadOps[F, A]`, `BindOps[F, A]`, ...
import scalaz.syntax.monad._
val oi: Option[Option[Int]] = Some(Some(1))
// Expands to: ToBindOps(io).join
oi.join
For some degree of backwards compatibility with Scalaz 6, the über-import of import scalaz.Scalaz._
will import all implicit conversions that provide syntax (as well as type class instances and other
functions). However, we recommend to review usage of this and replace with more focussed imports.
Standalone Type Class Usage
Type classes should be directly usable, without first needing to trigger implicit conversions. This might be desirable to reduce the runtime or cognitive overhead of the pimped types, or to define your own pimped types with a syntax of your choosing.
- The methods in type classes have been curried to maximize type inference.
- Derived methods, based on the abstract methods in a type class, are defined in the type class itself.
- Each type class companion object is fitted with a convenient
apply
method to obtain an instance of the type class.
// Equivalent to `implicitly[Monad[Option]]`
val O = Monad[Option]
// `bind` is defined with two parameter sections, so that the type of `x` is inferred as `Int`.
O.bind(Some(1))(x => Some(x * 2))
def plus(a: Int, b: Int) = a + b
// `Apply#lift2` is a function derived from `Apply#ap`.
val plusOpt = O.lift2(plus)
Type Class Instance Dependencies
Type class instances may depend on other instances. In simple cases, this is as straightforward as adding an implicit parameter (or, equivalently, a context bound), to the implicit method.
implicit def optionMonoid[A: Semigroup]: Monoid[Option[A]] = new Monoid[Option[A]] {
def append(f1: Option[A], f2: => Option[A]): Option[A] = (f1, f2) match {
case (Some(a1), Some(a2)) => Some(Semigroup[A].append(a1, a2))
case (Some(a1), None) => f1
case (None, Some(a2)) => f2
case (None, None) => None
}
def zero: Option[A] = None
}
Type class instances for 'transformers', such as OptionT
, present a more subtle challenge. OptionT[F, A]
is a wrapper for a value of type F[Option[A]]
. It allows us to write:
val ot = OptionT(List(Some(1), None))
ot.map((a: Int) => a * 2) // OptionT(List(Some(2), None))
The method OptionT#map
requires an implicit parameter of type Functor[F]
, whereas OptionT#flatMap
requires one of type Monad[F]
. The capabilities of OptionT
increase with those of F
. We need to encode
this into the type class instances for [a]OptionT[F[A]]
.
This is done with a hierarchy of type class implementation traits and a corresponding set of prioritized implicit methods.
In case of ambiguous implicits, Scala will favour one defined in a sub-class of the other. This is to avoid ambiguity when in cases like the following:
type OptionTList[A] = OptionT[List[A]]
implicitly[Functor[OptionTList]]
// Candidates:
// 1. OptionT.OptionTFunctor[List](implicitly[Functor[List]])
// 2. OptionT.OptionTMonad[List](implicitly[Functor[List]])
// #2 is defined in a subclass of the enclosing class of #1, so #2 is preferred.
Transformers and Identity
A stronger emphasis has been placed on transformer data structures (aka Monad Transformers). For example State
is now
a type alias for StateT[Id, A, B]
.
Id
is defined in the scalaz
package object as:
type Id[A] = A
Contributing
Documentation for contributors
Credits
Support for Scalaz development is provided by Jetbrains.
Thanks to Mark Harrah and the sbt contributors for providing our build tool.
Top Related Projects
Lightweight, modular, and extensible library for functional programming.
The Scala 3 compiler, also known as Dotty.
Generic programming for Scala
ZIO — A type-safe, composable library for async and concurrent programming in Scala
Powerful new number types and numeric abstractions for Scala.
Compositional, streaming I/O library for Scala
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot