In this post we’ll look at Scalaz Either. This is Scalaz’s version of the standard Scala Either. Before we look at Scalaz Either, we’ll look at Scala Either.
Represents a value of one of two possible types (a disjoint union.) Instances of Either are either an instance of Left or Right. … Convention dictates that Left is used for failure and Right is used for success.
With the definition out of the way, let’s look at some code. The example is modeled around the code in the official Scala Either docs.
Creating an Either
1 2 3 4 5 6 7 8
@ defparseInt(str: String): Either[String, Int] = { try { Right(str toInt) } catch { case e: Exception => Left(e getMessage) } } defined function parseInt
Next, let’s create a case each of success and failure.
Scala Either is not a monad and so you cannot use it in a for comprehensions.
1 2 3 4
@ for { n <- success } yield n res3: Either[String, Int] = Right(2)
NOTE:
Previously, Scala Either was not a monad so it couldn’t be used in for comprehensions. Now, it is a monad and can be used in for comprehensions.
Scalaz Either
A valid question to ask is why would one use Scalaz Either when Scala Either is a monad. The answer is that Scalaz Either is a lot more convenient and powerful compared to Scala Either. Let’s begin by refactoring parseInt to return a Scalaz Either.
Creating an Either
1 2 3 4 5 6 7 8 9 10 11 12
@ import scalaz._ import scalaz._ @ importScalaz._ importScalaz._ @ defparseInt(str: String): String \/ Int = { import scala.util.{Try, Success, Failure} Try { str toInt } match { caseSuccess(n) => n.right[String] caseFailure(e) => e.getMessage.left[Int] } } defined function parseInt
Next, let’s create a case each of success and failure.
1 2 3 4
@ val success = parseInt("2") success: String \/ Int = \/-(2) @ val failure = parseInt("apple") failure: String \/ Int = -\/("For input string: \"apple\"")
The return type of our function is indicated by String \/ Int. This means we may return a String on the left in case of failure and an Int on the right in case of success. We create right or left projections by calling right or left, respectively, and mentioning the type of the value that will be on the other side. For example, we call right[String] because the left side is a String. The right projection is indicated by \/- and left projection is indicated by -\/.
Using a for Comprehension
1 2 3 4
@ for { n <- success } yield n res12: String \/ Int = \/-(2)
Because Scalaz Either is also a monad, it can be used in a for comprehension.
Akin to Scala Either, Scalaz Either also lets you check for left or right by calling isLeft or isRight, respectively.
Ternary Operator
1 2
@ success ? "YES" | "NO" res15: String = "YES"
Scalaz Either provides you with a getOrElse which you can use to as a ternary operator using its symbolic representation |.
Folding an Either
1 2 3 4 5 6 7 8 9 10
@ success fold( left => -1, right => right + 1 ) res16: Int = 3 @ failure fold( left => -1, right => right + 1 ) res17: Int = -1
Both Scala and Scalaz Either provide you with a fold method which run the first function if we have a left, or the second function if we have a right.
Converting to Validation
The single biggest difference between Scala Either and Scalaz Either is that Scalaz Either can be converted to other types like Validation, etc. For example, converting an Either to a Validation allows you to accumulate errors. As the code comments state:
A \/ B is also isomorphic to Validation[A, B]. The subtle but important difference is that Applicative instances for Validation accumulates errors (“lefts”)
We create a Validation by calling validation method on the Either instance. Depending on a left or right, we get either a Success or Failure.
Conclusion
Scalaz Either and Scala Either are pretty similar in the latest version of Scala (2.12, as of writing). Which one you decide to use depends upon your personal preference. My preference is to use Scalaz Either throughout my code if I am using other Scalaz features to maintain consistency.
In this post we’ll look at Scalaz Validation which you can use to validate the data in your system. Data validation is a part and parcel of software development; you have to check the data that comes into your system or it may lead to unexpected behavior and / or cause your system to fail. Scalaz provides you with Validation to validate your data. Validation is an applicative functor. An applicative functor has more structure than a functor but less than a monad.[1]
Motivating Example
So let’s say we have a Version class representing the major and minor version of our software like so:[2]
1
caseclassVersion(major: Int, minor: Int)
Then, a negative value in either of major or minor would be invalid. We could ensure that we never get a negative value in either the major or minor by using require like so:[3]
1 2 3 4
caseclassVersion(major: Int, minor: Int) { require(major >= 0, "major must be >= 0: %s".format(major)) require(minor >= 0, "minor must be >= 0: %s".format(minor)) }
The problem here is that we’ll have to handle exceptions and we don’t want side-effects. Let’s use Validation to do it in a more functional way.
@ import scalaz._ import scalaz._ @ importScalaz._ importScalaz._ // paste mode in Ammonite REPL is { .. CODE HERE .. } and not :paste @ { caseclassVersion(major: Int, minor: Int) objectVersion{ defcreateNew(major: Int, minor: Int): Validation[IllegalArgumentException, Version] = { val isValidMajor = (major >= 0) ? true | false val isValidMinor = (minor >= 0) ? true | false
(isValidMajor, isValidMinor) match { case (false, true) => newIllegalArgumentException("major < 0").failure case (true, false) => newIllegalArgumentException("minor < 0").failure case (false, false) => newIllegalArgumentException("major and minor < 0").failure case (true, true) => newVersion(major, minor).success } } } } defined classVersion defined objectVersion @ Version.createNew(-1, -1) res5: Validation[IllegalArgumentException, Version] = Failure( java.lang.IllegalArgumentException: major and minor < 0 ) @ Version.createNew(1, 1) res6: Validation[IllegalArgumentException, Version] = Success(Version(1, 1))
What we’ve done here is add a createNew to the companion object of Version that returns a Validation. Validating the input can result in either a Success or Failure. We create a Success by calling success on the value and a Failure by calling failure on the value. Once we have a Validation, there are numerous ways in which we can deal with it.
Providing Default Values
1 2 3 4 5 6
@ val invalid = Version.createNew(-1, -1) invalid: Validation[IllegalArgumentException, Version] = Failure( java.lang.IllegalArgumentException: major and minor < 0 ) @ valdefault = invalid | newVersion(1, 0) default: Version = Version(1, 0)
There’s a convenient | operator (getOrElse) that lets you provide a default value if the result of the validation is a Failure. Here we are assigning the value 1 to major and 0 to minor.
Folding a Validation
1 2 3 4 5 6 7 8 9 10 11 12
@ val valid = Version.createNew(1, 0) valid: Validation[IllegalArgumentException, Version] = Success(Version(1, 0)) @ valid fold( f => s"Validation failed because ${f getMessage}".println, v => s"Version is $v".println ) "Version is Version(1,0)" @ invalid fold( f => s"Validation failed because ${f getMessage}".println, v => s"Version is $v".println ) "Validation failed because major and minor < 0"
Akin to a disjunction, it’s possible to fold a Validation. Failure will be the first argument and Success will be the second. If you want to fold just on the Success part, you can use foldRight.
NOTE:
Both fold and foldRight should return values. Here I’ve just printed out the values to the console which is a side-effect. I’ve done this to keep the examples simple.
defcreateNew(major: Int, minor: Int) = { (isValidMajor(major).toValidationNel |@| isValidMinor(minor).toValidationNel) { Version(_, _) } } } } defined classVersion defined objectVersion
When we started building the Version example, we used pattern matching to check if the major and minor versions were correct. However, there’s a more succinct way to collect all the errors. In the example above, we’ve turned isValidMajor and isValidMinor into methods that return a Validation instead of simply a Boolean.
The magic is in createNew. Here we convert the Validation into a NonEmptyList. toValidationNel wraps the Failure in a NonEmptyList but keeps the Success as-is. The |@| is an ApplicativeBuilder which lets us combine the failures together. If we get all successes, we construct a Version out of it. Let’s see this in action:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
@ val bothInvalid = Version.createNew(-1, -1) bothInvalid: Validation[NonEmptyList[IllegalArgumentException], Version] = Failure( NonEmpty[java.lang.IllegalArgumentException: major < 0,java.lang.IllegalArgumentException: minor < 0] )
@ val majorInvalid = Version.createNew(-1, 0) majorInvalid: Validation[NonEmptyList[IllegalArgumentException], Version] = Failure( NonEmpty[java.lang.IllegalArgumentException: major < 0] )
@ val minorInvalid = Version.createNew(1, -1) minorInvalid: Validation[NonEmptyList[IllegalArgumentException], Version] = Failure( NonEmpty[java.lang.IllegalArgumentException: minor < 0] )
So, in case of both the major and minor being invalid, we get a non-empty list with both the errors in it. This is very convenient and also very extensible. If you need to add an extra check, you can write a new isValidXXX method and make it return a Validation. You can then use the ApplicativeBuilder to your advantage. Using simply booleans would need checking a large number of possible cases.
Mapping Success and Failure
1 2 3 4 5
@ val errMsgs = bothInvalid bimap( f => f map { _.getMessage }, identity, ) errMsgs: Validation[NonEmptyList[String], Version] = Failure(NonEmpty[major < 0,minor < 0])
It’s possible to map over Success and Failure and apply any transformations you want. For example, we represent errors with IllegalArgumentExceptions and we may be coding a rest API and we’d like to send back the strings representing the errors. In the example above, I’ve used bimap to map Success and Failure. For every failure, I am extracting the string using getMessage and leaving the success as-is by using identity.
1 2 3 4 5
@ val theVersion = bothValid bimap( f => f map { _.getMessage }, identity, ) theVersion: Validation[NonEmptyList[String], Version] = Success(Version(1, 1))
Similar to bimap is map which just maps the Success part of the Validation.
Remember that bimap and map return a Validation. They don’t extract values out of it. You’ll have to use fold, etc. to get the values out.
Running a Side-Effect on Success
1 2
@ bothValid foreach { v => s"Begin build process for version $v".println } "Begin build process for version Version(1,1)"
foreach lets you run a side-effecting function if the result of the Validation is a Success. For example, using the Version, you could begin a build process or similar.
Checking if it is a Success or Failure
1 2 3 4 5 6 7 8 9 10 11
@ val isSuccess = bothValid isSuccess isSuccess: Boolean = true
@ val isFailure = bothInvalid isFailure isFailure: Boolean = true
There are numerous ways in which we can check if the Validation resulted in a Success or a Failure. The simplest way is to use one of isSuccess or isFailure. Similarly, exists will return true if the validation is a success value satisfying the given predicate and forall will return true if the validation is a failure value or the success value satisfies the given predicate.
Converting to Other Datatypes
1 2 3 4 5 6 7 8
@ val list = bothValid toList list: List[Version] = List(Version(1, 1))
// alter an existing key @ m1.alter("b") { maybeValue => maybeValue some { v => some(v + 1) } none { some(0) } } res4: Map[String, Int] = Map("a" -> 1, "b" -> 3)
alter lets you change the values associated with keys. In the example, we’re altering the value associated with key b. Since there may or may not be a value associated with b, we get an Option which we’ve named as maybeValue. We then use the some { ... } none { ... } construct to either add 1 in case there’s a value or initialize the key with 0. Also, since we need to return Options, we use some and none again.
intersectWith lets you calculate the intersection of two maps. It expects a function that will accept values v1 and v2 from both the maps and return a new value which will become the value associated with the key in the new map. If you want to analyze the key before returning the value, you can use intersectWithKey which expects a function (k, v1, v2) => { ... }.
unionWith lets you calculate the union of two maps and expects a function that will accept values v1 and v2 from both maps and return a new value which will become the value associated with the key in the new map. Similarly, there’s unionWithKey which will pass the key as the first argument to the function.
insertWith lets you insert a new key-value pair into the map. It also expects a function which will take values v1 and v2 as arguments to deal with cases where the key you’re trying to add already exists.
Conclusion
This brings us to the end of the post on MapOps. These extra functions make it very easy to work with an immutable Map by providing extra functionality not present in the Scala core library.
In this post we’ll look at BooleanOps and the goodies it provides to work with Booleans. As always, we’ll go straight to examples.
Unless
1 2 3 4 5 6 7 8 9 10 11 12 13
@ import scalaz._ import scalaz._ @ importScalaz._ importScalaz._ @ val t = true t: Boolean = true @ val f = false f: Boolean = false // executes the given side-effect if this boolean value is false @ t unless "this won't print".println
@ f unless "this will print".println "this will print"
As the comment for unless states:
Executes the given side-effect if this boolean value is false.
A mnemonic to remember the working is: “Unless the value is true, execute the side-effecting function”.
When
1 2 3 4 5
// executes the given side-effect if this boolean value is true @ t when "this will print".println "this will print"
@ f when "this won't print".println
The “opposite” of unless is when which executes the function when the value is true. As the comment for when states:
Executes the given side-effect if this boolean value is true.
A mnemonic to remember the working is: “When the value is true, execute the side-effecting function”.
Folding a Boolean
1 2 3 4
@ t fold[String]("this will be returned when true", "this will be returned when false") res8: String = "this will be returned when true" @ f fold[String]("this will be returned when true", "this will be returned when false") res9: String = "this will be returned when false"
fold lets you decide what value to return depending on whether it is true or false.
Converting to an Option
1 2 3 4
@ t option "this will create a Some() with this string in it" res10: Option[String] = Some("this will create a Some() with this string in it") @ f option "this will result in a None" res11: Option[String] = None
option lets us convert a Boolean into an Option in a type-safe manner. A true results in a Some containing the value passed to option whereas a false results in an Option of whatever the type of the argument is.
Ternary Operator
1 2 3 4
@ t ? "true" | "false" res13: String = "true" @ f ? "true" | "false" res14: String = "false"
Scalaz also provides a ternary operator to work with Booleans. The ternary operator is actually a combination of ? and |. ? is the conditional operator that results in the creation of an object of Conditional and | is a method of that object.
?? returns the given argument if the value is true, otherwise, the zero element for the type of the given argument. In our case, the “zero” element for List is an empty List.
!? is the opposite of ?? and returns the argument if the value is false or the zero element otherwise.
Conclusion
This brings us to the end of our post on BooleanOps. There’s a lot more functions provided but I’ve chosen to cover those which I feel will be the most useful.
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.