Scalaz features for everyday usage part 1: Typeclasses and Scala extensions
Most of you have probably heard of the great Javascript book: Javascript the good parts. In the same light I’d like to show some stuff from Scalaz which are really great to use in everyday projects, without having to dive into the (at least for me) scary inner workings of Scalaz. For this first part we’ll dive into a number of useful typeclasses. In future parts we’ll look at stuff like Monad Transformers, Free monads, Validation etc.
For these examples we’ll use the scala REPL. So if you want to play along start scala and load the scala library:
scala> :require /Users/jos/.ivy2/cache/org.scalaz/scalaz-core_2.11/bundles/scalaz-core_2.11-7.2.1.jar Added '/Users/jos/.ivy2/cache/org.scalaz/scalaz-core_2.11/bundles/scalaz-core_2.11-7.2.1.jar' to classpath. scala> import scalaz._ import scalaz._ scala> import Scalaz._ import Scalaz._
In this first article we’ll look at the following typeclasses from the Scalaz library:
- Equals typeclass: for typesafe equals operators.
- Order typeclass: for somewhat more typesafe ordering
- Enum typeclass: to create feature rich enumerations
Besides that we’ll also look at a couple of simple extensions Scalaz adds to some of the types provided by the standard Scala library. We won’t look at everything Scalaz adds, but just at a couple of extensions to Option and Boolean.
Useful typeclasses
With typeclasses you can easily add functionality to existing classes (see Pimp my library pattern). Scalaz comes with a couple on useful typeclasses out of the box you can immediately use.
Typesafe Equals operator
Scalaz provides a typesafe equals operator, which will throw a compile error when comparing invalid types. So while == and != in Scala will allow you to compare a String and an Int using the Scalaz === and =/= operators will result in a compile time error:
scala> 1 == 1 res6: Boolean = true scala> 1 === 1 res7: Boolean = true scala> 1 == "1" res8: Boolean = false scala> 1 === "1" <console>:14: error: type mismatch; found : String("1") required: Int 1 === "1"
Scalaz, provides the following set of operators, which behavior can be easily seen from the function implementation.
final def ===(other: F): Boolean = F.equal(self, other) final def /==(other: F): Boolean = !F.equal(self, other) final def =/=(other: F): Boolean = /==(other) final def ≟(other: F): Boolean = F.equal(self, other) final def ≠(other: F): Boolean = !F.equal(self, other)
Order typeclass
This is a very simple typeclass which provides more typesafe ordering. Just like with the Equals operators we can now catch comparison of two different types at compile time:
scala> 1 < 4d res25: Boolean = true scala> 1 lte 4d <console>:14: error: type mismatch; found : Double(4.0) required: Int 1 lte 4d scala> 1 ?|? 1 res31: scalaz.Ordering = EQ scala> 1 ?|? 2 res32: scalaz.Ordering = LT scala> 1 ?|? 2d <console>:14: error: type mismatch; found : Double(2.0) required: Int 1 ?|? 2d
Scalaz provides the following set of operators for this:
final def <(other: F): Boolean = F.lessThan(self, other) final def <=(other: F): Boolean = F.lessThanOrEqual(self, other) final def >(other: F): Boolean = F.greaterThan(self, other) final def >=(other: F): Boolean = F.greaterThanOrEqual(self, other) final def max(other: F): F = F.max(self, other) final def min(other: F): F = F.min(self, other) final def cmp(other: F): Ordering = F.order(self, other) final def ?|?(other: F): Ordering = F.order(self, other) final def lte(other: F): Boolean = F.lessThanOrEqual(self, other) final def gte(other: F): Boolean = F.greaterThanOrEqual(self, other) final def lt(other: F): Boolean = F.lessThan(self, other) final def gt(other: F): Boolean = F.greaterThan(self, other)
Enum typeclass
With the Scalaz Enum type it is very easy to create enumerators, which have more functionality than the ones in the standard Scala or Java libraries. It provides a number of functions to traverse through an Enum, and helps in creating new ones, of subset of ones. Scalaz provides the following set of functions:
final def succ: F = F succ self final def -+-(n: Int): F = F.succn(n, self) final def succx: Option[F] = F.succx.apply(self) final def pred: F = F pred self final def ---(n: Int): F = F.predn(n, self) final def predx: Option[F] = F.predx.apply(self) final def from: EphemeralStream[F] = F.from(self) final def fromStep(step: Int): EphemeralStream[F] = F.fromStep(step, self) final def |=>(to: F): EphemeralStream[F] = F.fromTo(self, to) final def |->(to: F): List[F] = F.fromToL(self, to) final def |==>(step: Int, to: F): EphemeralStream[F] = F.fromStepTo(step, self, to) final def |-->(step: Int, to: F): List[F] = F.fromStepToL(step, self, to)
A very nice example can be found here at stackoverflow: http://stackoverflow.com/questions/28589022/enumeration-concept-in-scala-which-option-to-take, which however requires a couple of small changes to get all the Scalaz goodies. The following code shows how to use this enumeration:
scala> import scalaz.Ordering._ import scalaz.Ordering._ scala> :paste // Entering paste mode (ctrl-D to finish) case class Coloring(val toInt: Int, val name: String) object Coloring extends ColoringInstances { val RED = Coloring(1, "RED") val BLUE = Coloring(1, "BLUE") val GREEN = Coloring(1, "GREEN") } sealed abstract class ColoringInstances { import Coloring._ implicit val coloringInstance: Enum[Coloring] with Show[Coloring] = new Enum[Coloring] with Show[Coloring] { def order(a1: Coloring, a2: Coloring): Ordering = (a1, a2) match { case (RED, RED) => EQ case (RED, BLUE | GREEN) => LT case (BLUE, BLUE) => EQ case (BLUE, GREEN) => LT case (BLUE, RED) => GT case (GREEN, RED) => GT case (GREEN, BLUE) => GT case (GREEN, GREEN) => EQ } def append(c1: Coloring, c2: => Coloring): Coloring = c1 match { case Coloring.RED => c2 case o => o } override def shows(c: Coloring) = c.name def zero: Coloring = Coloring.RED def succ(c: Coloring) = c match { case Coloring.RED => Coloring.BLUE case Coloring.BLUE => Coloring.GREEN case Coloring.GREEN => Coloring.RED } def pred(c: Coloring) = c match { case Coloring.GREEN => Coloring.BLUE case Coloring.BLUE => Coloring.RED case Coloring.RED => Coloring.GREEN } override def max = Some(GREEN) override def min = Some(RED) } } // Exiting paste mode, now interpreting. defined class Coloring defined object Coloring defined class ColoringInstances
Now we can use all the functions defined in the Scalaz enum ops:
scala> import Coloring._ import Coloring._ scala> RED res0: Coloring = Coloring(1,RED) scala> GREEN res1: Coloring = Coloring(1,GREEN) scala> RED |-> GREEN res2: List[Coloring] = List(Coloring(1,RED), Coloring(1,BLUE), Coloring(1,GREEN)) scala> RED succ warning: there was one feature warning; re-run with -feature for details res3: Coloring = Coloring(1,BLUE) scala> RED -+- 1 res4: Coloring = Coloring(1,BLUE) scala> RED -+- 2 res5: Coloring = Coloring(1,GREEN)
Nice right? This is a really great way of creating flexible and feature rich enumerations.
Standard classes extensions
Like we said in the beginning of this article, we’d look at how Scalaz make the standard Scala library more feature rich by adding functionality to some of its standard classes.
More fun with Options
With the Optional typeclass Scalaz makes working with Scala Options easier. For instance it provides function to make construction easier:
scala> Some(10) res11: Some[Int] = Some(10) scala> None res12: None.type = None scala> some(10) res13: Option[Int] = Some(10) scala> none[Int] res14: Option[Int] = None
What you’ll see is that the resulting type of these function is an Option[T] instead of Some or None. You might wonder why this is useful, but look at the following: Say we have a list of Options, over which we want to fold:
scala> val l = List(Some(10), Some(20), None, Some(30)) l: List[Option[Int]] = List(Some(10), Some(20), None, Some(30)) scala> l.foldLeft(None) { (el, z) => el.orElse(z) } <console>:22: error: type mismatch; found : Option[Int] required: None.type l.foldLeft(None) { (el, z) => el.orElse(z) }
This will fail, because our fold expects the result to be a None.type and not an Option. When we use the Scalaz versions, it works as expected:
scala> l.foldLeft(none[Int]) { (el, z) => el.orElse(z) } res19: Option[Int] = Some(10)
And Scalaz wouldn’t be Scalaz without introducing some new operators.
// Alternative for getOrElse scala> Some(10) | 20 res29: Int = 10 scala> none | 10 res30: Int = 10 // Ternary operator scala> Some(10) ? 5 | 4 res31: Int = 5 // ~ operator: Returns the item contained in the Option if it is defined, otherwise, the zero element for the type A scala> some(List()) res32: Option[List[Nothing]] = Some(List()) scala> ~res32 res33: List[Nothing] = List() scala> some(List(10)) res34: Option[List[Int]] = Some(List(10)) scala> ~res34 res35: List[Int] = List(10)
Nothing too complex, just some helper functions. There is a lot of other stuff surrounding Options in the Scalaz library, but that is a bit out of scope for this article.
More Boolean functionality
Scalaz also adds some functionality to the Boolean type.
# Ternary operations are back! scala> true ? "This is true" | "This is false" res45: String = This is true scala> false ? "This is true" | "This is false" res46: String = This is false # Returns the given argument if this is `true`, otherwise, the zero element for the type of the given argument. scala> false ?? List(120,20321) res55: List[Int] = List() scala> true ?? List(120,20321) res56: List[Int] = List(120, 20321)
And a whole list of additional operators for binary arithmetics:
// Conjunction. (AND) final def ∧(q: => Boolean) = b.conjunction(self, q) // Conjunction. (AND) final def /\(q: => Boolean) = ∧(q) // Disjunction. (OR) final def ∨(q: => Boolean): Boolean = b.disjunction(self, q) // Disjunction. (OR) final def \/(q: => Boolean): Boolean = ∨(q) // Negation of Disjunction. (NOR) final def !||(q: => Boolean) = b.nor(self, q) // Negation of Conjunction. (NAND) final def !&&(q: => Boolean) = b.nand(self, q) // Conditional. final def -->(q: => Boolean) = b.conditional(self, q) // Inverse Conditional. final def <--(q: => Boolean) = b.inverseConditional(self, q) // Bi-Conditional. final def <-->(q: => Boolean) = b.conditional(self, q) && b.inverseConditional(self, q) // Inverse Conditional. final def ⇐(q: => Boolean) = b.inverseConditional(self, q) // Negation of Conditional. final def ⇏(q: => Boolean) = b.negConditional(self, q) // Negation of Conditional. final def -/>(q: => Boolean) = b.negConditional(self, q) // Negation of Inverse Conditional. final def ⇍(q: => Boolean) = b.negInverseConditional(self, q) // Negation of Inverse Conditional. final def <\-(q: => Boolean) = b.negInverseConditional(self, q)
For instance:
scala> true /\ true res57: Boolean = true scala> true /\ false res58: Boolean = false scala> true !&& false res59: Boolean = true
More information on additional functions
In this short article we’ve only shown you a couple of the additonal functions provided by Scalaz. If you want more information, the easiest is to just lookup the sources, which in this case, provide quite helpful information e.g:
- scalaz.syntax.std.BooleanOps
- scalaz.syntax.std.ListOps
- scalaz.syntax.std.MapOps
- scalaz.syntax.std.OptionOps
- scalaz.syntax.std.StringOps
Some examples:
List fun
# get the tail as an option scala> List(10,20,30) res60: List[Int] = List(10, 20, 30) scala> res60.tailOption res61: Option[List[Int]] = Some(List(20, 30)) scala> List() res64: List[Nothing] = List() scala> res64.tailOption res65: Option[List[Nothing]] = None # intersperse the list with additional elements scala> List(10,20,30) res66: List[Int] = List(10, 20, 30) scala> res66.intersperse(1) res68: List[Int] = List(10, 1, 20, 1, 30) # from list to List[List] of all possibilities scala> List('a','b','c','d').powerset res71: List[List[Char]] = List(List(a, b, c, d), List(a, b, c), List(a, b, d), List(a, b), List(a, c, d), List(a, c), List(a, d), List(a), List(b, c, d), List(b, c), List(b, d), List(b), List(c, d), List(c), List(d), List())
Map fun
# alter one entry in a safe manner res77: scala.collection.immutable.Map[Char,Int] = Map(a -> 10, b -> 20) scala> res77.alter('a')(f => f |+| some(5)) res78: Map[Char,Int] = Map(a -> 15, b -> 20) # intersect two maps, and determine which value to keep of the keys that intersect scala> val m1 = Map('a' -> 100, 'b' -> 200, 'c' -> 300) m1: scala.collection.immutable.Map[Char,Int] = Map(a -> 100, b -> 200, c -> 300) scala> val m2 = Map('b' -> 2000, 'c' -> 3000, 'd' -> 4000) m2: scala.collection.immutable.Map[Char,Int] = Map(b -> 2000, c -> 3000, d -> 4000) scala> m1.intersectWith(m2)((m1v,m2v) => m2v) res23: Map[Char,Int] = Map(b -> 2000, c -> 3000) scala> m1.intersectWith(m2)((m1v,m2v) => m1v) res24: Map[Char,Int] = Map(b -> 200, c -> 300)
String fun
# Make a string plural (in a somewhat naive way) scala> "Typeclass".plural(1) res26: String = Typeclass scala> "Typeclass".plural(2) res27: String = Typeclasss scala> "Day".plural(2) res28: String = Days scala> "Weekly".plural(2) res29: String = Weeklies # safely parse booleans, bytes, shorts, longs, floats, double and ints scala> "10".parseDouble res30: scalaz.Validation[NumberFormatException,Double] = Success(10.0) scala> "ten".parseDouble res31: scalaz.Validation[NumberFormatException,Double] = Failure(java.lang.NumberFormatException: For input string: "ten")
Conclusion
I hoped you liked this first short introduction into Scalaz. And as you’ve seen these simple functions already provide a lot of added value, without having to dive into the very complex inner workings of Scalaz. The pattern used here is just a simple TypeClass pattern used to add functionality to some standard Scala functionality.
In the next article, we’ll look at some more complex functionality when we look at Monad Transformers.
Reference: | Scalaz features for everyday usage part 1: Typeclasses and Scala extensions from our JCG partner Jos Dirksen at the Smart Java blog. |