rubicon-project / rubiz   0.4.0

BSD 2-clause "Simplified" License GitHub

Library to augment Scalaz with handy syntax and missing conversions.

Scala versions: 2.12 2.11


Build Status scaladoc

Table of Contents


Provides much needed syntax and missing conversions for Scalaz 7.1.


Add the following to your build.sbt file:

libraryDependencies += "com.rubiconproject" %% "rubiz" % "0.4.+"

Import all the additional syntax with import rubiz.syntax.all._. Specific subsets of syntax can be imported if preferred (eg, import rubiz.syntax.either._).



import scalaz.Catchable
import scalaz.syntax.catchable._
import scalaz.effect.IO
import scalaz.concurrent.Task
import rubiz.syntax.catchable._


Check the result inside Catchable to see if it matches your predicate. If it doesn't, the left becomes your provided value.

  .ensure(new Exception("Can't make a user without a name."))(_.nonEmpty)
// res0: String = username


attempt, but it will only catch/map throwables for which the function is defined. If the function doesn't match for the throwable it will re-throw. A common use case is to map IO (user or DB) exceptions that you have a better type or message for on the left, rethrowing ones you didn't expect.

(IO(throw new java.sql.SQLException).attemptSome {
    case sqlE: java.sql.SQLException => "Computer says no."
// res1: scalaz.\/[String,Nothing] = -\/(Computer says no.)


Lets you define an exception handler on the Catchable that maintains the same type.

(IO[Int](throw new IllegalArgumentException)
  .except(e => IO(0))
// res2: Int = 0


Like except but only executes where the function is defined. Has similar use cases to attemptSome, but when you have a default you want to use instead of a Throwable transformation.

(IO[Int](throw new java.sql.SQLException).exceptSome {
    case sqlE: java.sql.SQLException => IO(0)
// res3: Int = 0


Like finally, but only runs when there was an exception.

(try {
  Task.delay(throw new Exception())
    .onException(Task.delay(println("THERE WAS A FIREFIGHT!")))
} catch {
  case _: Throwable => println("Or something.")
// Or something.


Generalizes finally for all Catchable, not just IO.

(try {
  Task.delay(throw new Exception())
    .ensuring(Task.delay(println("THERE WAS A FIREFIGHT!")))
} catch {
  case _: Throwable => println("Or something.")
// Or something.

  .ensuring(Task.delay(println("THERE WAS A FIREFIGHT!")))
// res6: Int = 0


import scalaz.concurrent.Task
import rubiz.syntax.task._
import scala.concurrent.duration._


Wraps timing information up with the result of the task. If the task failed, there isn't a result to wrap up with, and no timing information will be available. This is most useful for local logging of timing information.

(Task.delay(List("Australia", "Japan"))
  .withTiming           // Task[(FiniteDuration, List[String])]
  .map {
      case (timing, result) =>
        println(s"${result.length} country names were returned in ${timing.toMillis} ms.")
// 2 country names were returned in 1 ms.
// res8: List[String] = List(Australia, Japan)


Useful for side effecting the duration of a task to external services, generally a metrics backend or logging service. This logs the duration regardless of the success of the task.

(Task.delay(List("hello", "world"))
  .withSideEffectTiming(timing => println(s"${timing.toMillis} ms run, to the metrics service!"))  // Task[List[String]]
// 4 ms run, to the metrics service!
// res9: List[String] = List(hello, world)


Apply a timeout of time to t; if the timeout occurs, the resulting TimeoutException includes a message including label and time. Like scalaz.concurrent.Task.timed but with a non-null, useful error message in the exception.

  .labeledTimeout(2.millis, "silly example")
// res10: scalaz.\/[Throwable,Unit] = -\/(java.util.concurrent.TimeoutException: The 'silly example' task timed out after 2 milliseconds.)


leftMap for a Task. Useful for reporting a different exception than the one actually created by the failure.

( Exception("Esoteric nonsense."))
  .failMap(_ => new Exception("Contextual description of what happened."))
// res11: scalaz.\/[Throwable,Nothing] = -\/(java.lang.Exception: Contextual description of what happened.)


Allows you to handle errors and map the successes to a new value.

  .attemptFold(_ => "Failure")(_ ++ "es")
// res12: String = Successes
(Task.delay[String](throw new Exception("Explosion"))
  .attemptFold(_ => "The explosion was contained.")(_ ++ "es")
// res13: String = The explosion was contained.


Run your function as a side effect if the task is successful and pass the original return value through. Particularly useful for logging.

  .peek(b => b match {
    case true => println("Element was found.")
    case false => println("Element wasn't found.")
// Element was found.
// res14: Boolean = true


Run your function as a side effect if the task fails and pass the original Throwable through. Particularly useful for logging.

(Task.delay[Boolean](throw new Exception("I can't search this list!"))
  .peekFail(_ => println("What is an element, really?"))
// What is an element, really?
// res15: scalaz.\/[Throwable,Boolean] = -\/(java.lang.Exception: I can't search this list!)


Ensure that a resource is "closed" when a task completes, regardless of whether it's successful. A CanClose instance must be scope to call using. If the object to be closed isn't then you'll need to define a CanClose instance.

class CloseableThing extends { def close: Unit = println("Not so fast! I have been closed.") }
// defined class CloseableThing

Task.delay(new CloseableThing).using { closeableThing =>
  throw new Exception("All your resources are lost to chaos")
// Not so fast! I have been closed.
// res16: scalaz.\/[Throwable,Nothing] = -\/(java.lang.Exception: All your resources are lost to chaos)


import scalaz.\/
import scalaz.syntax.either._
import rubiz.syntax.either._


Turns your \/[Throwable, A] into a Task[A]. Useful when you're trying to compose Tasks and you want to mix in an Either.

(List("USA", "Canada")
  .right[Throwable]     // \/[Throwable, List[String]]
  .toTask               // Task[List[String]]
// res17: List[String] = List(USA, Canada)


Allows you to convert an Either to any Monad that has an Applicative and Catchable instance. This operates like toTask but is more generic.

// import scalaz.concurrent.Task
("Some Name"
  .right[Throwable] // \/[Throwable, String]
  .toM[Task]        // Task[String]
// res19: String = Some Name

// import scalaz.effect.IO
(new Exception("Users do bad things")
  .left[String] // \/[Throwable, String]
  .toM[IO]      // IO[String]
  .attempt      // IO[\/[Throwable, String]]
// res21: scalaz.\/[Throwable,String] = -\/(java.lang.Exception: Users do bad things)


try is a reserved word, so we've resorted to backticks. If you've got an alternative suggestion, we'd love to hear it.

import scala.util.Try
import rubiz.syntax.`try`._


If you're using Scalaz you'd probably rather be working with an Either/\//Disjunction than a Try.

val badTry = Try(throw new Exception("No really, users."))
// badTry: scala.util.Try[Nothing] = Failure(java.lang.Exception: No really, users.)

// res22: scalaz.\/[Throwable,Nothing] = -\/(java.lang.Exception: No really, users.)


If you're in streams-land and want to go directly from a Try to a Task, this sugars you on over there. Useful when using non-Scalaz libs with Scalaz streams.

val okTry = Try("My examples get worse as time goes on")
// okTry: scala.util.Try[String] = Success(My examples get worse as time goes on)
// res23: String = My examples get worse as time goes on


Run sbt test to build the project and exercise the unit test suite.

Continuous Integration

TravisCI builds on every published commit on every branch.


Rubiz is versioned with semver and released to sonatype. Open an issue on Github if you feel there should be a release published that hasn't been. You can run sbt tut to regenerate documentation locally if you modify the docs in the /main/tut directory.

For those with permission to release:

  • Install gpg and generate a key. Upload that key to a public key server.
    • Mac users can brew install gpg pinentry-mac to get the tools needed.
  • Create a Sonatype credentials file.
  • Run sbt release
  • Push the newly created tags and version bump commits to rubicon-project/rubiz.