Created
October 4, 2011 14:58
-
-
Save debasishg/1261857 to your computer and use it in GitHub Desktop.
a brief rant about |@| in scalaz
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* |@| is a helper function that helps you accumulate applicative functors. It gives you an ApplicativeBuilder (it's part of | |
* the implementation though) through which you accumulate your applicatives just as you would using the Builder pattern of | |
* the GoF in the OO world. Once you have all the applicatives you can pass it a function that will be applied to all the | |
* values that you have accumulated so far. e.g. | |
*/ | |
scala> (1.some |@| 2.some) apply {_ + _} | |
res1: Option[Int] = Some(3) | |
/** | |
* Here we accumulate 2 Option values and then apply the summation over the accumulated applicatives. Note the arity of the | |
* function needs to match the number of applicatives you accumulate. | |
* | |
* Typical to scalaz, |@| is pimped into all type constructors for which applicatives are defined. e.g. List, Option etc. | |
* Hence you can do the same with Lists as well .. | |
*/ | |
scala> val a, b = List(1) | |
a: List[Int] = List(1) | |
b: List[Int] = List(1) | |
scala> (a |@| b) (_ + _) | |
res2: List[Int] = List(2) | |
/** | |
* So the main excitement about such builders is that they give you a nice declarative syntax with infix notation and | |
* abstracts the heavy duty stuff behind very cool abstractions like Functors and Applicatives. And the best part is that | |
* they encourage you to write nice compositional code. | |
* | |
* Have a look at this snippet that chains a series of validations on a domain object through |@| .. | |
*/ | |
// using Validation as an applicative | |
// can be combined to accumulate exceptions | |
def makeTrade(account: Account, instrument: Instrument, refNo: String, market: Market, | |
unitPrice: BigDecimal, quantity: BigDecimal) = | |
(validUnitPrice(unitPrice).liftFailNel |@| | |
validQuantity(quantity).liftFailNel) { (u, q) => | |
Trade(account, instrument, refNo, market, u, q) | |
} | |
/** | |
* Here's a summary of what we do above. Validates 2 fields of the domain model Trade and if the validations pass then a | |
* concrete Trade object is created. Otherwise *all* errors are accumulated and lifted into a Non Empty List. Try doing this | |
* with a typical imperative statement based syntax. You will have a much more verbose syntax and bigger surface area of | |
* your abstraction. | |
* | |
* All |@| does is accumulate Applicatives and apply the accumulated parts to the function that you provide. But I said | |
* compositional :-) .. hence |@| composes nicely with other functional abstractions. Since |@| is pimped into an | |
* Applicative, we define Validation as an instance of Apply typeclass - so Validation plays nicely with |@|. And this | |
* extension is not closed. You can define your own abstractions, implement them as instances of the typeclass and use |@| | |
* to chain them. | |
*/ | |
/** | |
* >>>> Question: What I don't follow is what that function gets (?) in case validation fails and the error-list is | |
* produced. | |
* | |
* Validation is an Applicative Functor. Here we are composing a few of them (each individual Validation object) to form | |
* another Applicative. So what we get finally is an Applicative. Now note the methods map and flatMap in the trait | |
* Validation: | |
*/ | |
def map[B](f: A => B): Validation[E, B] = this match { | |
case Success(a) => Success(f(a)) | |
case Failure(e) => Failure(e) | |
} | |
def flatMap[EE >: E, B](f: A => Validation[EE, B]): Validation[EE, B] = this match { | |
case Success(a) => f(a) | |
case Failure(e) => Failure(e) | |
} | |
/** | |
* These are the bind methods that do the chaining of the Validation instances. | |
* | |
* The passed in function gets invoked *only* in the success case. And the final applicative is a success only when the | |
* individual validations are successes. So the function is not invoked at all if any of the validations fail. | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment