Scala

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:

  1. Equals typeclass: for typesafe equals operators.
  2. Order typeclass: for somewhat more typesafe ordering
  3. 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.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button