typelevel / scalaz-contrib   0.1.5

MIT License Website GitHub

Interoperability libraries & additional data structures and instances for Scalaz

Scala versions: 2.10

scalaz-contrib

Interoperability libraries & additional data structures and instances for Scalaz

Build Status

Usage

This library is currently available for Scala 2.11 only. For 2.10 builds, use version 0.1.5.

To use the latest version, include the following in your build.sbt:

libraryDependencies ++= Seq(
  "org.typelevel" %% "scalaz-contrib-210"        % "0.2",
  "org.typelevel" %% "scalaz-contrib-validation" % "0.2",
  "org.typelevel" %% "scalaz-contrib-undo"       % "0.2",
  // currently unavailable because there's no 2.11 build of Lift yet
  // "org.typelevel" %% "scalaz-lift"               % "0.2",
  "org.typelevel" %% "scalaz-nscala-time"        % "0.2",
  "org.typelevel" %% "scalaz-spire"              % "0.2"
)

For the in-progress features, use the following:

resolvers += Resolver.sonatypeRepo("snapshots")

and depend on version 0.3-SNAPSHOT instead.

Examples

Scala 2.10+

You can now use type class instances for new data types in the standard library:

scala> import scalaz._
import scalaz._

scala> import scalaz.contrib.std.utilTry._
import scalaz.contrib.std.utilTry._

scala> Monad[scala.util.Try]
res1: scalaz.Monad[scala.util.Try] = scalaz.contrib.std.TryInstances1$$anon$1@19ae3dd5

The instance for Scala's Future is in scalaz proper. Try remains here.

Validation DSL

There are a couple of useful validators and converters, as well as a DSL for checking and transforming values.

import scalaz.contrib.Checker
import scalaz.contrib.validator.all._, scalaz.contrib.converter.all._
import scalaz.std.list._

val c = Checker.check("2012-12-20".toList)

scala> c.checkThat(notEmpty("must be non-empty")(_)).
     |   map(_.mkString("")).
     |   convertTo(date("yyyy-MM-dd", "must be a valid date")).
     |   toValidation
res0: Validation[NonEmptyList[String],Date] = Success(Thu Dec 20 00:00:00 CET 2012)

scala> c.checkThat(notEmpty("must be non-empty")(_)).
     |   map(_.mkString("")).
     |   convertTo(uuid("must be a valid UUID")).
     |   toValidation
res1: Validation[NonEmptyList[String],Date] = Failure(NonEmptyList(must be a valid UUID))

Undo

Originally by Gerolf Seitz (@gseitz).

import scalaz.contrib.undo.UndoT
import UndoT._
import scalaz.std.option._

val result = for {
  one           <- hput[Option, Int](1)
  two           <- hput[Option, Int](2)
  three         <- hput[Option, Int](3)
  twoAgain      <- undo[Option, Int]
  four          <- hput[Option, Int](4)
  twoAgainAgain <- undo[Option, Int]
  fourAgain     <- redo[Option, Int]
} yield ()

scala> result.exec(1)
res0: Option[Int] = Some(4)

Library bindings

This project provides bindings (instances) for the following libraries:

  • spire 0.7.4
  • nscala_time 1.0.0

spire

Spire provides powerful abstractions for numeric programming in Scala, including a full-stack hierarchy of algebraic type classes such as Semigroup, Monoid, and Ring. Scalaz only has the former two, but instead lots of instances. This library provides mappings between type classes where it makes sense.

There are two modes of conversion, manual and automatic:

  • Importing scalaz.contrib.spire._ enables the manual mode. It adds the methods asSpire and asScalaz on type class instances.
  • Importing either scalaz.contrib.spire.conversions.toSpire._ or scalaz.contrib.spire.conversions.toScalaz._ (importing both will lead to anarchy) enables the automatic mode. It provides implicit conversions for type class instances. This mode does not provide conversions for two-operator classes (i.e. for Rig and Semiring).

It is possible (but not recommended) to enable both modes.

To understand which conversions "make sense", consider the kinds of type classes offered by scalaz and spire:

  • Scalaz provides only one-operator classes, namely Semigroup and Monoid. To make a distinction between additive and multiplicative operations, scalaz uses tags. Hence, a Semigroup[A] denotes an unspecified operation, and Semigroup[A @@ Multiplication] a multiplicative operation.
  • Spire provides one- and two-operator classes. The one-operator classes come in three flavours, e.g. Semigroup, AdditiveSemigroup and MultiplicativeSemigroup (same for Monoid, ...). As in scalaz, a plain Semigroup conveys nothing about the type of operation, whereas the other two should be used for additive and multiplicative operations, respectively. Spire's two-operator classes inherit from the additive and multiplicative variants, e.g. Semiring extends AdditiveSemigroup and MultiplicativeSemigroup. Also, these classes should guarantee that these two distinct operations relate to each other.
  • Both scalaz and spire provide some notion of equality and ordering.

Thus, in manual mode, the following conversions are available:

  • from scalaz one-operator to spire one-operator:
    S.asSpire // Semigroup → Semigroup
    S.asSpireAdditive // ... → AdditiveSemigroup
    S.asSpireMultiplicative // ... → MultiplicativeSemigroup
    
    SMult.asSpire // Semigroup @@ Multiplication → MultiplicativeSemigroup
    
    E.asSpire // Equal → Eq
    O.asSpire // Order → Order
  • from spire one-operator to scalaz one-operator:
    // Semigroup and AdditiveSemigroup → Semigroup
    // MultiplicativeSemigroup → Semigroup @@ Multiplication
    S.asScalaz
    
    // Eq → Equal
    // Order → Order
    O.asScalaz

These operations are also available in automatic mode, without the need to call asXY.

  • from scalaz one-operator to spire two-operator:

    // (Semigroup, Semigroup @@ Multiplication) → Semiring
    (S, SMult).asSpire

The other direction is for free since the two-operator classes extend the one-operator classes. This operation is not available in automatic mode, since there is no guarantee that two independent scalaz instances obey the necessary laws to form a two-operator class.

Of course, the above mentioned conversions also work for Monoid and Rig.