learning Scalaz: day 13

in
e.e d3si9n

Yesterday we skimmed two papers by Jeremy Gibbons and quickly looked at origami programming and applicative traversal. Instead of reading something, why don't we focus on using Scalaz today.

implicits review

Scalaz makes heavy use of implicits. Both as a user and an extender of the library, it's important to have general idea on where things are coming from. Let's quickly review Scala's imports and implicits!

In Scala, imports are used for two purposes:
1. To include names of values and types into the scope.
2. To include implicits into the scope.

Implicits are for 4 purposes that I can think of:
1. To provide typeclass instances.
2. To inject methods and operators. (static monkey patching)
3. To declare type constraints.
4. To retrieve type information from compiler.

Implicits are selected in the following precedence:
1. Values and converters accessible without prefix via local declaration, imports, outer scope, inheritance, and current package object. Inner scope can shadow values when they are named the same.
2. Implicit scope. Values and converters declared in companion objects and package object of the type, its parts, or super types.

import scalaz._

Now let's see what gets imported with import scalaz._.

First, the names. Typeclasses like Equal[A] and Functor[F[_]] are implemented as trait, and are defined under scalaz package. So instead of writing scalaz.Equal[A] we can write Equal[A].

Next, also the names, but type aliases. scalaz's package object declares most of the major type aliases like @@[T, Tag] and Reader[E, A], which is treated as a specialization of ReaderT transformer. Again, these can also be accessed as scalaz.Reader[E, A] if you want.

Finally, idInstance is defined as typeclass instance of Id[A] for Traverse[F[_]], Monad[F[_]] etc, but it's not relevant. By virtue of declaring an instance within its package object it will be available, so importing doesn't add much. Let's check this:

scala> scalaz.Monad[scalaz.Id.Id]
res1: scalaz.Monad[scalaz.Id.Id] = scalaz.IdInstances$$anon$1@fc98c94

No import needed, which is a good thing. So, the merit of import scalaz._ is for convenience, and it's optional.

import Scalaz._

What then is import Scalaz._ doing? Here's the definition of Scalaz object:

package scalaz
 
object Scalaz
  extends StateFunctions        // Functions related to the state monad
  with syntax.ToTypeClassOps    // syntax associated with type classes
  with syntax.ToDataOps         // syntax associated with Scalaz data structures
  with std.AllInstances         // Type class instances for the standard library types
  with std.AllFunctions         // Functions related to standard library types
  with syntax.std.ToAllStdOps   // syntax associated with standard library types
  with IdInstances              // Identity type and instances

This is quite a nice way of organizing the imports. Scalaz object itself doesn't define anythig and it just mixes in the traits. We are going to look at each traits in detail, but they can also be imported a la carte, dim sum style. Back to the full course.

StateFunctions

Remember, import brings in names and implicits. First, the names. StateFunctions defines several functions:

package scalaz
 
trait StateFunctions {
  def constantState[S, A](a: A, s: => S): State[S, A] = ...
  def state[S, A](a: A): State[S, A] = ...
  def init[S]: State[S, S] = ...
  def get[S]: State[S, S] = ...
  def gets[S, T](f: S => T): State[S, T] = ...
  def put[S](s: S): State[S, Unit] = ...
  def modify[S](f: S => S): State[S, Unit] = ...
  def delta[A](a: A)(implicit A: Group[A]): State[A, A] = ...
}

By bringing these functions in we can treat get and put like a global function. Why? This enables DSL we saw on day 7:

for {
  xs <- get[List[Int]]
  _ <- put(xs.tail)
} yield xs.head

std.AllFunctions

Second, the names again. std.AllFunctions is actually a mixin of traits itself:

package scalaz
package std
 
trait AllFunctions
  extends ListFunctions
  with OptionFunctions
  with StreamFunctions
  with math.OrderingFunctions
  with StringFunctions
 
object AllFunctions extends AllFunctions

Each of the above trait bring in various functions into the scope that acts as a global function. For example, ListFunctions bring in intersperse function that puts a given element in ever other position:

scala> intersperse(List(1, 2, 3), 7)
res3: List[Int] = List(1, 7, 2, 7, 3)

It's ok. Since I personally use injected methods, I don't have much use to these functions.

IdInstances

Although it's named IdInstances, it also defines the type alias Id[A] as follows:

  type Id[+X] = X

That's it for the names. Imports can bring in implicits, and I said there are four uses for the implicits. We mostly care about the first two: typeclass instances and injected methods and operators.

std.AllInstances

Thus far, I have been intentionally conflating the concept of typeclass instances and method injection (aka enrich my library). But the fact that List is a Monad and that Monad introduces >>= operator are two different things.

One of the most interesting design of Scalaz 7 is that it rigorously separates the two concepts into "instance" and "syntax." Even if it makes logical sense to some users, the choice of symbolic operators can often be a point of contention with any libraries. Libraries and tools such as sbt, dispatch, and specs introduce its own DSL, and their effectiveness have been hotly debated. To make the matter complicated, injected methods may conflict with each other when more than one DSLs are used together.

std.AllInstances is a mixin of typeclass instances for built-in (std) data structures:

package scalaz.std
 
trait AllInstances
  extends AnyValInstances with FunctionInstances with ListInstances with MapInstances
  with OptionInstances with SetInstances with StringInstances with StreamInstances with TupleInstances
  with EitherInstances with PartialFunctionInstances with TypeConstraintInstances
  with scalaz.std.math.BigDecimalInstances with scalaz.std.math.BigInts
  with scalaz.std.math.OrderingInstances
  with scalaz.std.util.parsing.combinator.Parsers
  with scalaz.std.java.util.MapInstances
  with scalaz.std.java.math.BigIntegerInstances
  with scalaz.std.java.util.concurrent.CallableInstances
  with NodeSeqInstances
  // Intentionally omitted: IterableInstances
 
object AllInstances extends AllInstances

syntax.ToTypeClassOps

Next are the injected methods and operators. All of them are defined under scalaz.syntax package. syntax.ToTypeClassOps introduces all the injected methods for typeclasses:

package scalaz
package syntax
 
trait ToTypeClassOps
  extends ToSemigroupOps with ToMonoidOps with ToGroupOps with ToEqualOps with ToLengthOps with ToShowOps
  with ToOrderOps with ToEnumOps with ToMetricSpaceOps with ToPlusEmptyOps with ToEachOps with ToIndexOps
  with ToFunctorOps with ToPointedOps with ToContravariantOps with ToCopointedOps with ToApplyOps
  with ToApplicativeOps with ToBindOps with ToMonadOps with ToCojoinOps with ToComonadOps
  with ToBifoldableOps with ToCozipOps
  with ToPlusOps with ToApplicativePlusOps with ToMonadPlusOps with ToTraverseOps with ToBifunctorOps
  with ToBitraverseOps with ToArrIdOps with ToComposeOps with ToCategoryOps
  with ToArrowOps with ToFoldableOps with ToChoiceOps with ToSplitOps with ToZipOps with ToUnzipOps with ToMonadWriterOps with ToListenableMonadWriterOps

For example, [syntax.ToBindOps] implicitly converts F[A] where [F: Bind] into BindOps[F, A] that implements >>= operator.

syntax.ToDataOps

syntax.ToDataOps introduces injected methods for data structures defined in Scalaz:

trait ToDataOps extends ToIdOps with ToTreeOps with ToWriterOps with ToValidationOps with ToReducerOps with ToKleisliOps

IdOps methods are injected to all types, and are mostly there for convenience:

package scalaz.syntax
 
trait IdOps[A] extends Ops[A] {
  final def ??(d: => A)(implicit ev: Null <:< A): A = ...
  final def |>[B](f: A => B): B = ...
  final def squared: (A, A) = ...
  def left[B]: (A \/ B) = ...
  def right[B]: (B \/ A) = ...
  final def wrapNel: NonEmptyList[A] = ...
  def matchOrZero[B: Monoid](pf: PartialFunction[A, B]): B = ...
  final def doWhile(f: A => A, p: A => Boolean): A = ...
  final def whileDo(f: A => A, p: A => Boolean): A = ...
  def visit[F[_] : Pointed](p: PartialFunction[A, F[A]]): F[A] = ...
}
 
trait ToIdOps {
  implicit def ToIdOps[A](a: A): IdOps[A] = new IdOps[A] {
    def self: A = a
  }
}

Interestingly, ToTreeOps converts all data types to TreeV[A] injecting two methods:

package scalaz
package syntax
 
trait TreeV[A] extends Ops[A] {
  def node(subForest: Tree[A]*): Tree[A] = ...
  def leaf: Tree[A] = ...
}
 
trait ToTreeOps {
  implicit def ToTreeV[A](a: A) = new TreeV[A]{ def self = a }
}

So these are injected methods to create Tree.

scala> 1.node(2.leaf)
res7: scalaz.Tree[Int] = <tree>

The same goes for WriterV[A], ValidationV[A], ReducerV[A], and KleisliIdOps[A]:

scala> 1.set("log1")
res8: scalaz.Writer[String,Int] = scalaz.WriterTFunctions$$anon$26@2375d245
 
scala> "log2".tell
res9: scalaz.Writer[String,Unit] = scalaz.WriterTFunctions$$anon$26@699289fb
 
scala> 1.success[String]
res11: scalaz.Validation[String,Int] = Success(1)
 
scala> "boom".failureNel[Int]
res12: scalaz.ValidationNEL[String,Int] = Failure(NonEmptyList(boom))

So most of the mixins under syntax.ToDataOps introduces methods to all types to create Scalaz data structure.

syntax.std.ToAllStdOps

Finally, we have syntax.std.ToAllStdOps, which introduces methods and operators to Scala's standard types.

package scalaz
package syntax
package std
 
trait ToAllStdOps
  extends ToBooleanOps with ToOptionOps with ToOptionIdOps with ToListOps with ToStreamOps
  with ToFunction2Ops with ToFunction1Ops with ToStringOps with ToTupleOps with ToMapOps with ToEitherOps

This is the fun stuff. BooleanOps introduces shorthands for all sorts of things:

scala> false /\ true
res14: Boolean = false
 
scala> false \/ true
res15: Boolean = true
 
scala> true option "foo"
res16: Option[String] = Some(foo)
 
scala> (1 > 10)? "foo" | "bar"
res17: String = bar
 
scala> (1 > 10)?? {List("foo")}
res18: List[String] = List()

The option operator is very useful. The ternary operator looks like a shorter notation than if-else.

OptionOps also introduces something similar:

scala> 1.some? "foo" | "bar"
res28: String = foo
 
scala> 1.some | 2
res30: Int = 1

On the other hand ListOps introduced traditional Monad related things:

scala> List(1, 2) filterM {_ => List(true, false)}
res37: List[List[Int]] = List(List(1, 2), List(1), List(2), List())

a la carte style

Or, I'd like to call dim sum style, where they bring in a cart load of chinese dishes and you pick what you want.

If for whatever reason if you do not wish to import the entire Scalaz._, you can pick and choose.

typeclass instances and functions

Typeclass instances are broken down by the data structures. Here's how to get all typeclass instances for Option:

// fresh REPL
scala> import scalaz.std.option._
import scalaz.std.option._
 
scala> scalaz.Monad[Option].point(0)
res0: Option[Int] = Some(0)

This also brings in the "global" helper functions related to Option. Scala standard data structures are found under scalaz.std package.

If you just want all instances, here's how to load them all:

scala> import scalaz.std.AllInstances._
import scalaz.std.AllInstances._
 
scala> scalaz.Monoid[Int]
res2: scalaz.Monoid[Int] = scalaz.std.AnyValInstances$$anon$3@784e6f7c

Because we have not injected any operators, you would have to work more with helper functions and functions under typeclass instances, which could be exactly what you want.

Scalaz typeclass syntax

Typeclass syntax are broken down by the typeclass. Here's how to get injected methods and operators for Monads:

scala> import scalaz.syntax.monad._
import scalaz.syntax.monad._
 
scala> import scalaz.std.option._
import scalaz.std.option._
 
scala> 0.point[Option]
res0: Option[Int] = Some(0)

As you can see, not only Monad method was injected but also Pointed methods got in too.

Scalaz data structure syntax like Tree are also available under scalaz.syntax package. Here's how to load all syntax for both the typeclasses and Scalaz's data structure:

scala> import scalaz.syntax.all._
import scalaz.syntax.all._
 
scala> 1.leaf
res0: scalaz.Tree[Int] = <tree>

standard data structure syntax

Standard data structure syntax are broken down by the data structure. Here's how to get injected methods and operators for Boolean:

// fresh REPL
scala> import scalaz.syntax.std.boolean._
import scalaz.syntax.std.boolean._
 
scala> (1 > 10)? "foo" | "bar"
res0: String = bar

To load all the standard data structure syntax in:

// fresh REPL
scala> import scalaz.syntax.std.all._
import scalaz.syntax.std.all._
 
scala> 1.some | 2
res1: Int = 1

I thought this would be a quick thing, but it turned out to be an entire post.
We'll pick it up from here.