Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Scalaz Features for Everyday Usage Part 1: Typeclasses and Scala Extensions

DZone's Guide to

Scalaz Features for Everyday Usage Part 1: Typeclasses and Scala Extensions

Scalaz provides a multitude of additions to Scala functionality, including improved operators, feature rich enumerations, and boolean arithmetic.

· Java Zone
Free Resource

Learn how our document data model can map directly to how you program your app, and native database features like secondary indexes, geospatial and text search give you full access to your data. Brought to you in partnership with MongoDB.

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 things 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, the behavior of which 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 that provides more typesafe ordering. Just like with the Equals operators we can now catch a 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, or subsets 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'll look at how Scalaz makes 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 a 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 functions 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 way is to just look at the sources, which in this case, provide quite helpful information. For example:

  • scalaz.syntax.std.BooleanOps
  • scalaz.syntax.std.ListOps
  • scalaz.syntax.std.MapOps
  • scalaz.syntax.std.OptionOps
  • scalaz.syntax.std.StringOps

Here are some more 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 hope 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.

Discover when your data grows or your application performance demands increase, MongoDB Atlas allows you to scale out your deployment with an automated sharding process that ensures zero application downtime. Brought to you in partnership with MongoDB.

Topics:
scala ,operator

Published at DZone with permission of Jos Dirksen, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}