In this post we’ll look at TryOps and the goodies it provides to work with scala.util.Try. To recap, here’s what Try does:
The Try type represents a computation that may either result in an exception, or return a successfully computed value. It’s similar to, but semantically different from the scala.util.Either type.
Converting to a Disjunction
1 2 3 4 5 6 7 8 9 10 11 12
@ import scalaz._ import scalaz._ @ importScalaz._ importScalaz._ @ import scala.util.Try import scala.util.Try // an operation that may potentially throw an exception @ val t1 = Try { "1".toInt } t1: Try[Int] = Success(1) // converting to a Scalaz disjunction @ val disjunction = t1 toDisjunction disjunction: Throwable \/ Int = \/-(1)
The result of a Try is either a Success or a Failure. This can very easily be translated to a Scalaz disjunction. A Success produces a right disjunction whereas a Failure produces a left disjunction.
Similarly, if this Try were a part of validating your data like checking values in a JSON object, you can convert this to a Scalaz Validation.
Converting to a ValidationNel
1 2
@ val nel = t1 toValidationNel nel: ValidationNel[Throwable, Int] = Success(1)
ValidationNel is useful for accumulating errors. We’ll cover all of this in coming posts.
Conclusion
This brings us to the end of the post on TryOps. In coming posts we’ll look at Validation type which lets us represent, as you might have guessed, the result of validating an input. Similarly, if we want to accumulate all the results of validating inputs, we use ValidationNel. Both of these are subjects of coming posts.
In this post we’ll look at TupleOps and the goodies it provides to work with Tuples. There’s no TupleOps.scala file in the GitHub repo because the file is generated by GenerateTupleW. You can see the code for TupleOps if you’re using an IDE like IntelliJ. There’s a number of TupleNOps classes like Tuple2Ops[A, B], etc. all the way to Tuple12Ops[A, B].
With that said, let’s jump into examples. We’ll look at what Tuple2Ops provides us. Everything is analogous to Tuple3Ops, etc. It’s just the number of arguments to the methods that will change.
fold in Tuple2Ops takes a function which accepts 2 arguments i.e. equal to the arity of the tuple. Here, we’re folding the tuple and adding together its two Int elements but you can do whatever you want.
mapElements in Tuple2Ops takes 2 functions as arguments, 1 for each element of the tuple. The functions are then applied to their corresponding elements and the result is returned as a Tuple. In the example above, we’re just multiplying both the elements by 2.
This is different from the map from the standard library. The map from the standard library only operates on the last element of the tuple.
1 2
@ (1, 2) map (_ * 2) res5: (Int, Int) = (1, 4)
Conclusion
That’s the end of the post on TupleOps. TupleOps‘s convenience methods can be used to manipulate tuples easily.
Scalaz provides a convenient plural method on String to get its plural form. Going over the docs for plural:
Returns the same String value if the given value is 1 otherwise pluralises this String by appending an “s” unless this String ends with “y” and not one of [“ay”, “ey”, “iy”, “oy”, “uy”] in which case the ‘y’ character is chopped and “ies” is appended.
This explains why the plural of “dress” was “dresss” with 3 “s”; plural simply appended an “s”. Nonetheless, this is a convenient method.
charsNel converts the String into an Option[NonEmptyList[Char]]. This method is useful if the string represents a sequence of actions to be taken as characters. For example, “OCOC” could mean “Open, Close, Open, Close” or something and charsNel would convert this into an Option of NonEmptyList[Char]. You can then iterate over the characters and take whatever action you want to.
Parsing
Scalaz provides convenient methods for parsing a String into Boolean, Int, etc. The advantage of using these over standard Scala parsing methods is that these methods return a Validation. You can then fold the Validation object to see whether the value was successfully parsed or resulted in an error. This is good for functional programming as you don’t have to catch exceptions. We’ll first look at the Scala way and then the Scalaz way.
1 2 3 4 5 6 7 8 9 10 11
// Scala way @ "1".toInt res8: Int = 1 @ "x".toInt java.lang.NumberFormatException: For input string: "x" ... // Scalaz way @ "1".parseInt.fold(err => "failed parse".println, num => num.println) 1 @ "x".parseInt.fold(err => "failed parse".println, num => num.println) "failed parse"
It’s also possible to parse values to BigDecimal and BigInt.
1 2
@ "3.14159265358979323846".parseBigDecimal.fold(err => "failed parse".println, num => num.println) 3.14159265358979323846
Conclusion
This covers the convenience methods Scalaz provides for dealing with Strings. The parseXXX methods are the most useful as they avoid having to deal with an exception.
In this post we’ll look at OptionOps and the goodies it provides to work with Options. We’ll jump straight into examples.
Creating Options
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
@ import scalaz._ import scalaz._ @ importScalaz._ importScalaz._ // Some @ some(13) res2: Option[Int] = Some(13) // Calling some on a value // Notice something about res variable? ;) @ 13.some res9: Option[Int] = Some(13) // None @ none res3: Option[Nothing] = None @ none[Int] res4: Option[Int] = None
Extracting Values
1 2 3 4 5 6 7 8 9 10 11 12
// using unary ~ operator @ ~ res2 res5: Int = 13 // using some / none @ res2 some { _ * 2 } none { 0 } res6: Int = 26 // using | operator @ res2 | 0 res7: Int = 13 // extracting value from none @ ~ res4 res8: Int = 0
The unary ~ extracts the value from the Option or returns the zero value for that type. For example, in case of Int it’ll return the value in the Option or return 0. The some{ .. } none { .. } construct lets you specify what value to return in case of Some and None. I am multiplying the value by 2 and thus returning 26. As you can see, Scalaz provides with more expressive and type-safe way to create and extract values from Option.
Notice that the type of all the resulting variables is Option instead of Some or None. This is more type-safe than using None because you’d get None.type, and the type inferencer would allow the type to be Option[Int] or Option[String] whereas none[Int] is guaranteed to be an Option[Int]. The type inferencer will enforce this.
// we'll have fun with these values @ val o = some("option") o: Option[String] = Some("option") @ val n = none[String] n: Option[String] = None
// None to right disjunction @ n toRightDisjunction "onTheLeft" res10: String \/ String = -\/("onTheLeft") // Folding the disjunction prints the value on the left @ res10 fold(l => s"Left: $l".println, r => s"Right: $r".println) "Left: onTheLeft"
// Some to right disjunction @ o toRightDisjunction "onTheLeft" res11: String \/ String = \/-("option") // Folding the disjunction prints the value on the right @ res11 fold(l => s"Left: $l".println, r => s"Right: $r".println) "Right: option"
// default values, similar to unary ~ @ n.orZero res12: String = ""
Scalaz provides us with a disjunction which is a replacement for Scala Either. I’ve only introduced disjunction here and will cover it later in a post of its own. Trying to convert a None to a right disjunction doesn’t work. We get back a left disjunction as indicated by -\/. Similarly, converting a Some to right disjunction works, as indicated by \/-. Scalaz also provides a ternary operator to work with Options.
Conclusion
This sums up, non-exhaustively, how we can use the convenience methods that Scalaz provides on top of Options that lets us code in more expressive and type-safe way.
In this post we’ll look at how to create enum using Scalaz. However, we’ll first look at how to create enum using standard Scala library.
Enum - Scala Way
An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. Common examples include compass directions (values of NORTH, SOUTH, EAST, and WEST) and the days of the week.[1]
There are a couple of ways in which you can create enum in Scala.
Defines a finite set of values specific to the enumeration. Typically these values enumerate all possible forms something can take and provide a lightweight alternative to case classes.
Let’s see an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@ objectDirectionextendsEnumeration{ typeDirection= Value valNORTH, EAST, WEST, SOUTH = Value } defined objectDirection @ importDirection._ importDirection._ @ Direction withName "NORTH" res2: Value = NORTH @ Direction.values foreach println NORTH EAST WEST SOUTH
Advantages:
Lightweight.
Easy to use.
Iterable.
Disadvantages:
They only exist as unique values. You cannot add behavior to them.
Using Case Objects
Enum can also be created using sealed trait. A sealed trait requires all the classes or traits extending it be in the same file in which it is defined.
Each value is an instance of its own type and it's possible to add behavior to them.
Disadvantages:
A case object generates a lot more code than `Enumeration`.
Not iterable.
Enum - Scalaz Way
Scalaz allows creating enum using Enum trait. Scalaz enums are more powerful than the standard Scala enums. Let’s see some examples of creating Scalaz enum[2]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
@ import scalaz._ import scalaz._ @ importScalaz._ importScalaz._ // as a List @ 'a' |-> 'z' res3: List[Char] = List( 'a', 'b', 'c', 'd', 'e', 'f', ... // as a Scalaz stream @ 'a' |=> 'z' res4: EphemeralStream[Char] = scalaz.EphemeralStream$$anon$5@2fb4925c
Scalaz also provides us with succ and pred to get the successor and predecessor.
defsucc(s: Size): Size = s match { caseSMALL => MEDIUM caseMEDIUM => LARGE caseLARGE => SMALL }
defpred(s: Size): Size = s match { caseSMALL => LARGE caseMEDIUM => SMALL caseLARGE => MEDIUM }
defzero: Size = SMALL
overridedefshows(s: Size) = s.name
overridedefmax= Some(LARGE)
overridedefmin= Some(SMALL) } } defined objectSize @ importSize._ importSize._ // step through @ SMALL -+- 1 res4: Size = Size(1, "MEDIUM") // as a list @ SMALL |-> LARGE res5: List[Size] = List(Size(0, "SMALL"), Size(1, "MEDIUM"), Size(2, "LARGE")) // show @ SMALL.println SMALL // max @ implicitly[Enum[Size]].max res6: Option[Size] = Some(Size(2, "LARGE")) // min @ implicitly[Enum[Size]].min res7: Option[Size] = Some(Size(0, "SMALL"))
Notice that since we’re using implicitly[], we don’t have to provide the exact name of the typeclass extending Enum[Size], the compiler will figure it out from the implicits within scope.
Conclusion
Although Scalaz Enum may seem like a long-winded way to do something trivial, they offer you a lot more flexibility and brevity over vanilla Scala enum. However, I believe that the full force of Scalaz Enum is needed only when you have ordered elements like size. For cases like compass directions, Scala enum are better-suited.
As always, there are implicits defined for Int, Float, etc. Calling println returns a String whereas calling show returns a Cord. Going over the source code for Cord:
A Cord is a purely functional data structure for efficiently storing and manipulating Strings that are potentially very long.
Why Use Show?
A reasonable question to ask is why use Show when we have a toString which produces a String representation of objects. The answer is that toSring doesn’t always produce a useful representation.
1 2
@ println(newThread()) Thread[Thread-185,5,main]
Showing a Thread
So, let’s create a Show for Thread.
1 2 3 4 5 6 7 8 9 10 11 12
@ implicitobjectThreadShowableextendsShow[Thread] { overridedefshows(t: Thread): String = s"Thread Id=${t.getId} name=${t.getName}" } defined objectThreadShowable @ val t = newThread() t: Thread = Thread[Thread-322,5,main] // Scala way @ println(t) Thread[Thread-322,5,main] // Scalaz way @ t.println ThreadId=352 name=Thread-322
Conclusion
Show is probably not that interesting and probably exists because there is Show class in Haskell[1]:
The instances of class Show are those types that can be converted to character strings (typically for I/O)
In this post we’ll look at how to implement ordering using Scalaz Order trait. However, before we do that, let’s step back a little and look at how we implement ordering using scala.math.Ordering
Sorting a Collection - Scala Way
Say we have a List and we’d like to sort it.[1] We can do so by calling the sorted method on it and it’ll work out-of-the-box for lists of Int, Float, etc. because there is an implicit in scala.math.Ordering.
Going over the documentation for scala.math.Ordering[2]:
Ordering is a trait whose instances each represent a strategy for sorting instances of a type. Ordering’s companion object defines many implicit objects to deal with subtypes of AnyVal (e.g. Int, Double), String, and others.
All the implicit objects in the companion object implement the scala.math.Ordering trait which extends the java.util.Comparator interface. Thus, they all have a compare method. What if we have types for which there is no sorting strategy in the companion object of scala.math.Ordering? We define our own like so:
1 2 3 4 5 6 7 8 9
@ caseclassSalary(amt: Float) defined classSalary @ implicitobjectSalaryOrderedextendsOrdering[Salary] { // we are using compare on type Float defcompare(a: Salary, b: Salary): Int = a.amt compare b.amt } defined objectSalaryOrdered @ List(Salary(999.0f), Salary(555.0f)).sorted res4: List[Salary] = List(Salary(555.0F), Salary(999.0F))
But we still don’t have operators like <, <=, etc. on type Salary.
1 2 3 4 5
@ Salary(999.0f) > Salary(555.0f) cmd3.sc:1: value > is not a member of ammonite.$sess.cmd0.Salary val res3 = Salary(999.0f) > Salary(555.0f) ^ CompilationFailed
To do that, the trait scala.math.Ordered would have to be mixed in.
And we know that if we’re working with a library, we do not have the liberty to mix a trait for our convenience. Also, Scala’s comparison operators are not type safe.
1 2
@ 1 > 2.0 res4: Boolean = false
Sorting a Collection - Scalaz Way
As stated before, Scalaz provides Order trait. Staying with our Salary example, let’s put Scalaz Order to use.
We’ve defined a way to order Salary objects using Scalaz Order trait via a typeclass. The implicit object needs to provide an implementation for order method which returns an Ordering value. Going over the code for Scalaz Ordering:
This Ordering is analogous to the Ints returned by scala.math.Ordering.
Now, we have comparison operators at our disposal. Here’s how we can compare Salary objects using the ?|? operator:
You can now also use sorted on a collection equally easily. Using Order[A].toScalaOrdering we can get back an object of type scala.math.Ordering which we can use to sort collections.
By using Scalaz Order trait, we can implement comparison between values in a type-safe and extensible way. There’s seamless transformation from Scalaz Order to scala.math.Ordering which lets us use the sorted method on collections.
In this post we’ll finally start using Scalaz. We’ll look at how to get Scalaz using sbt and look at how it provides us with type-safe equality checking.
I highly recommend Ammonite Scala REPL. It provides a lot of improvements over the standard REPL like being able to import library dependencies straight from the console. I’ll be using Ammonite REPL henceforth because it’ll help me keep the examples in the REPL. However, there isn’t any difference beyond how to get the dependencies.
Fire Up the REPL
No matter what your preferred REPL is, let’s get started.
Standard REPL
Start the REPL by executing sbt console. Then, execute the following:
1 2 3 4 5
scala> import scalaz._ import scalaz._
scala> importScalaz._ importScalaz._
Ammonite
Start the REPL by executing the amm command. Then, execute the following:
We can compare two values in Scala using == (double equals). The issue, however, is that it will let us compare unrelated types like a string with an integer and such a comparison would always yield false.
1 2
@ 1 == "1" res3: Boolean = false
The == operator that Scala provides is a null-safe comparison operator and not type-safe. What if we want type-safety, too? This is where Scalaz’s === (triple equals) comes in. It’ll complain when you try to compare unrelated types.
1 2 3 4 5 6 7
@ 1 === "1" cmd4.sc:1: typemismatch; found : String("1") required: Int val res4 = 1 === "1" ^ CompilationFailed
Similarly, we can check for inequalities. The Scala operator != is null-safe but not type-safe.
1 2
@ 1 != "1" res4: Boolean = true
Here’s the Scalaz way to check for inequality using =/= operator which is both type-safe and null-safe:
1 2 3 4 5 6 7
@ 1 =/= "1" cmd5.sc:1: typemismatch; found : String("1") required: Int val res5 = 1 =/= "1" ^ CompilationFailed
Under the Hoods
As always, there are type classes at play here. There is an Equal trait which provides an equal to check if the two values are equal and of the same type.
1
defequal(a1: F, a2: F): Boolean
Since all this magic is done using type classes, how about we put it to use and write code to compare two Person objects?
Scalaz === lets you check for equality in a type-safe way. More often than not, this is what you need. Trying to compare values of dissimilar types is usually not needed. Using === ensures that such comparisons lead to errors at compile-time instead of waiting for them to surface at run-time. This makes debugging much more efficient.
Polymorphism is a programming language feature that allows one interface to be used for a general class of actions.[1] Scalaz makes extensive use of ad-hoc polymorphism to provide its set of goodies. In this post I’ll cover ad-hoc polymorphism in detail and show you how you can implement ad-hoc polymorphism in Scala. I’ll also talk about parametric, and subtype polymorphism.
Parametric Polymorphism
In parametric polymorphism, the behavior of the function does not depend upon the type of the arguments passed to it. More formally[2],
Parametric polymorphism refers to when the type of a value contains one or more (unconstrained) type variables, so that the value may adopt any type that results from substituting those variables with concrete types.
Here, the argument xs has an unconstrained type A which can be anything and yet head would work. Then we call head with lists of concrete type Int, and String resulting in xs being of type List[Int] and List[String].
Subtype Polymorphism
Subtype polymorphism involves inheritance. More formally[3],
Subtype polymorphism is a form of type polymorphism in which a subtype is a data-type that is related to another data-type (the super-type) by some notion of substitutability.
As an example[4], consider a trait Plus which lets its subtype add itself with another of the same type.
The function plus[A <: Plus[A]] will work only with those arguments that are subtype of Plus. This is restrictive because for this to work, the trait needs to be mixed in at the time of defining whatever concrete type A represents.
Ad-hoc Polymorphism
In ad-hoc polymorphism the behavior of the function depends on what the type of the argument is. More formally[5],
Ad-hoc polymorphism refers to when a value is able to adopt any one of several types because it, or a value it uses, has been given a separate definition for each of those types.
The simplest way to do ad-hoc polymorphism is by overloading functions. For example, having plus(Int, Int), plus(Currency, Currency), etc. The other ways are by using type classes, and coercion.
Type Classes
I’ll rewrite the Plus[A] example using type classes:
Relating the above implementation to the formal definition, the implicit p was able to adopt one of CurrencyPlus or KilogramPlus depending on what arguments were passed. Thus the behavior of plus is polymorphic as its behavior comes from the definition of each of the types.
Coercion
The other way to implement ad-hoc polymorphism is coercion. Coercion refers to implicitly converting the argument into a type that the function expects. Here’s an example modeled around scala.runtime.RichInt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
scala> classAwesomeInt(val self: Int) { | defabsolute: Int = math.abs(self) | } defined classAwesomeInt
The absolute method is defined in AwesomeInt but what we have is a plain old Int. This Int gets transformed into an AwesomeInt automagically because we have an implicit conversion within scope. The Int was coerced into an AwesomeInt.
So we see how ad-hoc polymorphism allows us to extend classes to whose code we don’t have access. We defined absolute to work with Int which comes from the Scala core library.
Higher-Kinded Types (HKT)
In the post where we generalized sum function, we generalized it to work with a List. What if we want to generalize it even further so that it can work with not just list but anything? Here’s what we want to achieve:
1 2 3 4 5
// we have this defsum[A](xs: List[A])(implicit m: Monoid[A]): A = ???
// we want this defsum[M[_], A](xs: M[A])(implicit m: Monoid[A], f: FoldLeft[M]): A = ???
The difference between the two is that the first version, although polymorphic over types for which we have monoids defined, is still heavily tied to List. The second version instead will work with type that is FoldLeft. We’ll expand upon the sum example[6]. We’ll need the monoids and we’ll need to add a FoldLeft to make sum more generic.
You might wonder what the whole point of using so much polymorphism is. The answer is that it lets you inject new functionality into existing libraries without having access to their source code. To quote Martin Odersky[7]:
There’s a fundamental difference between your own code and libraries of other people: You can change or extend your own code, but if you want to use some other libraries you have to take them as they are … Scala has implicit parameters and conversions. They can make existing libraries much more pleasant to deal with.
So, by using all the polymorphism techniques, we can implement the “pimp my library” pattern.[8]
The Pimp my Library Pattern suggests an approach for extending a library that nearly does everything that you need but just needs a little more. It assumes that you do not have source code for the library of interest.
Conclusion
The polymorphism capabilities provided by Scala allow Scalaz to provide its set of features. By using HKT we can write code that truly generalizes across multiple types. It might seem like a long-winded way to do something so simple but as codebase gets larger and larger, the features that Scalaz provides out-of-the-box really help in eliminating boilerplate code. We haven’t seen how to use Scalaz, yet. These posts serve as a foundation to understand how Scalaz does what it does.
A lot of what Scalaz does is made possible by using ad-hoc polymorphism, traits, and implicits. I’ll explain how this works by borrowing from Nick Partridge’s talk on Scalaz.
Motivating Example
Let’s begin with a simple sum function that adds together all the elements in a List[Int].
1 2
scala> defsum(xs: List[Int]): Int = xs.foldLeft(0)( _ + _ ) defined function sum
The sum function above works only with List[Int]. If we want to sum together a List[Double], List[Float], or a List[String], we’d need a new implementation of sum. Our goal is to make sum function general so it would work with any of these.
Step 1 - Monoid
The first step towards generalizing sum is by using a monoid. A monoid is an algebraic structure with a single associative binary operation and an identity element.[1]. Since we are working with a List[Int], let’s create an IntMonoid.
1 2 3 4 5 6 7 8
scala> objectIntMonoid{ defmempty: Int = 0 defmappend(a: Int, b: Int): Int = a + b } defined objectIntMonoid
scala> defsum(xs: List[Int]) = xs.foldLeft(IntMonoid.mempty)(IntMonoid.mappend) defined function sum
mempty is the identity or the zero value, and mappend is the binary operation which produces another Int i.e. another value in the set. These names come from Haskell[2].
Step 2 - Generalizing the Monoid
Next, we’ll generalize the monoid by creating a Monoid[A] so that IntMonoid is just a monoid on Int.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
scala> traitMonoid[A] { defmempty: A defmappend(a: A, b: A): A } defined traitMonoid
scala> objectIntMonoidextendsMonoid[Int] { defmempty: Int = 0 defmappend(a: Int, b: Int) = a + b } defined objectIntMonoid
scala> defsum[A](xs: List[A], m: Monoid[A]): A = xs.foldLeft(m.mempty)(m.mappend) defined function sum
scala> sum(List(1, 2, 3), IntMonoid) res3: Int = 6
What we’ve done is create a general-purpose sum function whose working depends upon which monoid is passed to it. Now we can very easily sum a List[String] or a List[Double] by adding a corresponding monoid.
Step 3 - Make the Monoid Implicit
Next, we’ll make the monoid an implicit parameter to our sum function. We’ll also package our IntMonoid into a Monoid companion object and make it implicit. The reason for doing this is how Scala compiler resolves implicit values; it’ll look for implicit values in its scope. So, we bring IntMonoid within scope by importing from the Monoid companion object.
scala> traitMonoid[A] { defmempty: A defmappend(a: A, b: A): A } defined traitMonoid
scala> objectMonoid{ implicitobjectIntMonoidextendsMonoid[Int] { defmempty: Int = 0 defmappend(a: Int, b: Int) = a + b } } defined objectMonoid
scala> importMonoid._ importMonoid._
scala> defsum[A](xs: List[A])(implicit m: Monoid[A]): A = xs.foldLeft(m.mempty)(m.mappend) defined function sum
scala> sum(List(1, 2, 3)) res4: Int = 6
So, what we’ve done is create a general-purpose sum function that works as long as there is a corresponding implicit monoid within scope. This is made possible by using ad-hoc polymorphism. I’ll cover ad-hoc polymorphism briefly in this post and defer providing a detailed explanation for a later post.
Ad-hoc Polymorphism
Ad-hoc polymorphism is a type of polymorphism in which polymorphic functions are invoked depending on the different argument types. One way to implement ad-hoc polymorphism that we already know about is function overloading where a different “version” of the function is invoked depending on the type of arguments. This is what we’ve done in the post where there is only a single function but an implementation is provided for different types. In other words, sum only knows how to be invoked but the behavior is provided by monoids. Another way to implement ad-hoc polymorphism is coercion where the argument is converted into a type which the function expects.
So, by using ad-hoc polymorphism, Scalaz is able to provide general-purpose functions over existing types. Ad-hoc polymorphism is flexible in the sense that it lets you extend even those classes for which you do not have access to the source code i.e. classes from other libraries, etc.
I’ve been using Scalaz for a while now and I remember not having any guides that provide a gentle introduction. It was a lot of scouring around, reading source code, and watching tech talks that gave me the understanding that I have now. This series of posts is intended at filling that gap by providing simple, step-by-step tutorials that will help you get productive quickly without compromising on the functional programming concepts.
What is Scalaz?
The documentation for Scalaz (pronounced Scala-zee or Scala-zed) states:
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.
In a nutshell, Scalaz aims to do three things:
Provide new datatypes that are not present in the core Scala library
Provide new operations on existing types a.k.a. pimp the library
Provide general-purpose functions so that you don't have to re-write them
I’ll provide a quick example of each of these without going into any details.