Favor Disjunction over Validation

On July 20, 2014, in Scala, scalaz, by OleTraveler

What is Wrong

For validating data in Scala, Scalaz is the de facto tool for the job. The applicative behavior allows the programmer to accumulate errors from various data flows. However, I believe all the video and blog learning resources I have come across are fundamentally wrong in how they explain using Validation classes. Because of these resources, I have been using validation incorrectly for many years. Recently, I have come to the conclusion that the Validation class should only be used during the act of accumulating, that is for the |@| operator, and Disjunction ( \/ ) should be the main class used for validation and error checking.

An Example

Take for instance validating user input for a CreditCard described in the image below. In this example, we expect the string inputs of cardholder, number, expiration month and expiration year. The process should validate that the cardholder’s input only contain ASCII characters and that it has no more than 30 characters. The process should return both errors if both errors are found. For the number input, the process should first validate that there are only digits and then validate that the number passes the Luhn Algorithm. If the number input is not all digits, then we short circuit out of the Luhn Check. The process should validate individually that the expiration month and expiration year are Integers, converting them if they are, and that they are in a valid range. It should then validate against the combined value of month and year to ensure the expiration date is the current month or in the future. Finally, if no validation errors are found, the process will create and return a new instance of CreditCard, otherwise the process will return a list of all the errors found.

cc-validation

Functions Return Validation, an Incorrect Approach

An incorrect approach, one I have been using for years, is to code all validation functions to return a Validation object where the failure type is a NonEmptyList[ErrorMessage]. Having NonEmptyList[ErrorMessage] as the failure type allows the calling code to easily accumulate errors. In the example below, the validate method is taking 4 input strings and returning a CreditCard object if all the validation checks pass, otherwise it returns one or more ErrorMessages indicating a failure.

   type ErrorMessage = String
 
  def asciiOnly(str: String) : Validation[NonEmptyList[ErrorMessage],String] =
    if ("[\\x00-\\x7F]+".r.pattern.matcher(str).matches) str.success
    else "only ascii characters are allowed".failNel
 
  def maxStrLength(max: Int): (String) => Validation[NonEmptyList[ErrorMessage],String] = str =>
    if (str.length <= max) str.success
    else s"can not exceed ${max} characters".failNel
 
  def digitsOnly(str: String) : Validation[NonEmptyList[ErrorMessage], String] =
    if ("""^\d*$""".r.pattern.matcher(str).matches) str.success
    else s"only numbers are allowed".failNel
 
  def toInt(str: String) : Validation[NonEmptyList[ErrorMessage], Int] = try {
    str.toInt.success
  } catch {
    case e: NumberFormatException => "must be a number".failNel
  }
 
  def modTen(str: String) : Validation[NonEmptyList[ErrorMessage], String] = {
    def passesModTen(str: String): Boolean = ???
 
    if (passesModTen(str)) str.success
    else s"invalid number".failNel
  }
 
  def validMonth(m: Int) : Validation[NonEmptyList[ErrorMessage], Int] =
    if (m >= 1 && m <= 12) m.success
    else "invalid month".failNel
 
  def positiveNu(m: Int) : Validation[NonEmptyList[ErrorMessage], Int] =
    if (m > 0) m.success
    else "must be positive".failNel
 
  def validExpiration(currentMonth: Int, currentYear:Int) : (Int,Int) => Validation[NonEmptyList[ErrorMessage], (Int,Int)] = (month,year) =>
    if (year > currentYear || (year === currentYear && month >= currentMonth)) (month, year).success
    else "card has expired".failNel
 
  case class CreditCard(cardholder: String, number: String, expMonth: Int, expYear:Int)
 
  def validateCardHolder(cardholder: String): Validation[NonEmptyList[ErrorMessage], String] =
    (asciiOnly(cardholder) |@| maxStrLength(10)(cardholder)){ (s ,_)  => s }
 
  def validate(cardholder: String, number: String, expMonth: String, expYear: String) : Validation[NonEmptyList[ErrorMessage], CreditCard] = {
 
    /** Accumulate both */
    val cardHolderV = validateCardHolder(cardholder)
 
    /** Check digits, then modTen */
    val numberV = digitsOnly(number).flatMap(modTen(_))
 
    val validToday = validExpiration(7,2014)
 
    val monthYear = for {
      m <- toInt(expMonth).flatMap(validMonth(_))
      y <- toInt(expYear).flatMap(positiveNu(_))
      my <- validToday(m,y)
    } yield my
 
    /** If there were any errors, return them.  Otherwise create a credit card */
    (cardHolderV |@| numberV |@| monthYear) { (c,n,my) => CreditCard(c,n,my._1,my._2)}
 
  }
 

The most glaring coding error is the lack of separation of concern in the methods that do the low level validation. The validation methods have taken it upon themselves to return a NonEmptyList[ErrorMessage] in the failure position which does not clearly separate what these methods should be doing — which is returning the correct String if valid or one and only one ErrorMessage objects if invalid. The methods also should not be returning a Validation object since the object in the Failure position should not be required to have a Semigroup typeclass; it is just an ErrorMassage. Note: Although the Validation class does not explicitly require the type contained in the failure type has a Semigroup typeclass, the most interesting method on Validation `|@|` does require that the type contained in the failure type has a Semigroup typeclass. In other words, the only useful form of Validation is one where the Failure case type has a Semigroup typeclass.

Different Approach; Default to Disjunction

Scalaz provides the disjunction type \/[+A, +B] which is isomorphic to scala.Either[A,B] but unlike Either is right biased and integrates better with other Scalaz classes we will be using such as Validation and Klesli. Instead of Validation[NonEmptylist,T], the return type for the validating methods should be \/[ErrorMessage,T] for any validating method that will only return a single ErrorMessage or a \/[NonEmptyList[ErrorMessage],T] for any validating type that may return 1 or more ErrorMessage instances.

  type ErrorMessage = String
 
  def asciiOnly(str: String): ErrorMessage \/ String =
    if ("[\\x00-\\x7F]+".r.pattern.matcher(str).matches) \/-(str)
    else -\/("only ascii characters are allowed")
 
  def maxStrLength(max: Int): (String) => ErrorMessage \/ String = str =>
    if (str.length <= max) \/-(str)
    else -\/(s"can not exceed ${max} characters")
 
  def digitsOnly(str: String): ErrorMessage \/ String =
    if ( """^\d*$""".r.pattern.matcher(str).matches) \/-(str)
    else -\/(s"only numbers are allowed")
 
  def toInt(str: String): ErrorMessage \/ Int = try {
    \/-(str.toInt)
  } catch {
    case e: NumberFormatException => -\/("must be a number")
  }
 
  def modTen(str: String): ErrorMessage \/ String = {
    def passesModTen(str: String): Boolean = ???
 
    if (passesModTen(str)) \/-(str)
    else -\/(s"invalid number")
  }
 
  def validMonth(m: Int): ErrorMessage \/ Int =
    if (m >= 1 && m <= 12) \/-(m)
    else -\/("invalid month")
 
  def positiveNumber(m: Int): ErrorMessage \/ Int =
    if (m > 0) \/-(m)
    else -\/("must be positive")
 
  def validExpiration(currentMonth: Int, currentYear: Int): (Int, Int) => ErrorMessage \/ (Int, Int) = (month, year) =>
    if (year > currentYear || (year === currentYear && month >= currentMonth)) \/-((month, year))
    else -\/("card has expired")
 
  case class CreditCard(cardholder: String, number: String, expMonth: Int, expYear: Int)
 
  def validateCardHolder(cardholder: String): NonEmptyList[ErrorMessage] \/ String =
    (asciiOnly(cardholder).validation.toValidationNel |@| maxStrLength(10)(cardholder).validation.toValidationNel) { (s, _) => s}
      .disjunction
 
  def validate(cardholder: String, number: String, expMonth: String, expYear: String): NonEmptyList[ErrorMessage] \/ CreditCard = {
 
    /** Accumulate both */
    val cardHolderV = validateCardHolder(cardholder)
 
    /** Check digits, then modTen */
    val numberV = digitsOnly(number).flatMap(modTen(_))
 
    val validToday = validExpiration(7, 2014)
 
    val monthYear = for {
      my <- (toInt(expMonth).flatMap(validMonth(_)).validation.toValidationNel |@|
        toInt(expYear).flatMap(positiveNumber(_)).validation.toValidationNel) {
        (_, _)
      }
        .disjunction
      validMY <- validToday(my._1, my._2).leftMap(NonEmptyList(_)) //error return type of my is NonEmptyList[ErrorMessage]
    } yield validMY
 
    /** If there were any errors, return them.  Otherwise create a credit card */
    (cardHolderV.validation |@|
      numberV.validation.toValidationNel |@|
      monthYear.validation) { (c, n, my) => CreditCard(c, n, my._1, my._2)}
      .disjunction
 
  }

The resulting code, compared to the first example, is simpler and more concise at the low level validating methods. Code that calls these validating methods are no longer required to use NonEmptyList as the Semigroup implementation.

Notice how in the methods validateCardHolder and validate, we convert to Validation[NonEmptyList[ErrorMessage],T] only when we expect to accumulate errors and then immediately convert back to \/[NonEmptyList[ErrorMessage],T] when we are done accumulating in that current scope.

Disjunction can have lawful Monad instances. This allows Disjunction to play nicely with scalaz.Kleisli which will allow us to chain together types of (T) => ErrorMessage \/ U such that the output of function 1 can map to the input of function 2. I am hoping to produce another post about this subject in the future.

\/.flatMap is a valid method unlike Validation.flatMap which has been deprecated in Scalaz 7.1 for various reasons.

The conversion from \/[ErrorMessage,String] to Validation[NonEmptyList[ErrorMessage], String] using method validation.toValidationNel does suffer from being a bit verbose and maybe a bit too specific to the NonEmptyList implementation. This can be handled at the Scalaz library level or in the short term, we can add an implicit class to reduce the verbosity of the Disjunction conversions. That implicit class may look something like this.

object Disjunction {
 
  implicit class DisjunctionI[A, B](d: \/[A, B]) {

    /** I would prefer a method that required F[_] to have a semigroup typeclass.
     *  That is over my head at the moment. 
     */ 
    def validationF[F[_]](f: A => F[A]): Validation[F[A],B] =
      d match {
        case -\/(a) => Failure(f(a))
        case \/-(b) => Success(b)
      }
 
    def validationNel: Validation[NonEmptyList[A], B] = validationF(NonEmptyList(_))
 
    def leftNel: \/[NonEmptyList[A],B] = d.leftMap(NonEmptyList(_))
 
  }
}

One must also be careful not to use a (T) => \/[E,U] in \/[NonEmptyList[E],T].flatMap. Convert the \/[E,U] to a \/[NonEmptyList[]] as show in the validate method: validToday(my._1, my._2).leftMap(NonEmptyList(_)). This verbosity can also be reduced with an implicit class.

Code for this blog can be found in a Gist. For more examples favoring disjunction, consult my port of Scalaz Validation Contrib.

Tagged with:  

Pattern Matching vs Subtype Polymorphism

On May 30, 2013, in Scala, by OleTraveler

This post is an overview of the talk Living in a Post-Functional World by Daniel Spiewak. In ithe talks about The Expression Probmel which is defined as:

  • Define a datatype by case.
  • Add new Cases to the datatype.
  • Add new functions over the datatype
  • Don’t recompile (don’t change any existing code)

There are no clear solution to this problem, but there are different strategies.  In Scala, the options are pattern matching and subtype polymorphism.  Pattern matching allow adding a new function without altering existing codc, but alter existing code when adding new cases.   Whereas subtype polymorphism allows adding new cases without recompiling existing code, however existing code would need to be altered when adding function.

 

For example, here is Daniel’s example using Patter Matching:

 

  
object Pattern {

  sealed trait Expr
  case class Add(e1: Expr, e2: Expr) extends Expr
  case class Sub(e1: Expr, e2: Expr) extends Expr
  case class Num(n: Int) extends Expr

  def value(e: Expr): Int = e match {
    case Add(e1, e2) => value(e1) + value(e2)
    case Sub(e1, e2) => value(e1) - value(e2)
    case Num(n) => n
  }
  val expr = Add(Num(5), Sub(Num(50), Num(13)))

  val result = value(expr) //41
}

Using the above as a codebase, adding a function for pretty printing the expression would not require any of the existing code to be touched. However adding the new algrebaic type Mult would require the value function be modified to match on the Mult type. (Note that the Visitor’s pattern is a poor-mans pattern matching in Object Oriented languages).

Here is Daniel’s example of Subtype Polymorphism.

  
object Subtype {
  sealed trait Expr {
    def value: Int
  }

  case class Add(e1: Expr, e2: Expr) extends Expr {
    def value = e1.value + e2.value
  }

  case class Sub(e1: Expr, e2: Expr) extends Expr {
    def value = e1.value - e2.value
  }

  case class Num(n: Int) extends Expr {
    def value = n
  }
  val expr = Add(Num(5), Sub(Num(50), Num(12)))

  val result = expr.value
}

In order to add a pretty print method, all the case classes would have to be modified. However if we were to add the Multi class, none of the existing case classes would have to be touched.

So, when designing a system that is expected to expand with more cases, use subtype polymorphism, whereas if the system is expected to expand more with functions, use pattern matching.

Scalaz Validation: my personal cheat sheet

On April 25, 2012, in scalaz, Uncategorized, by OleTraveler

I use a few Scalaz Validaiton methods for chaining validations together: flatMap, |@|, and map.

The flatMap method is used to short circuit a validation. For instance, if a value is null or empty, there is no reason to validate whether or not the value has exceeded a max length.

The |@| is used to collect all validations.

The map method is used to convert a valid result into another valid result, useful during context change. Also failProject.map(_).validation is used to convert a failure into another type of failure. I use this, for instance, to change a Validation error which is a general error into a JSON formatted error which I can return from a web service.

I am always finding myself having to relearn what some of the scalaz Validation methods do, so here they are.  This is my interpretation of what each of the methods in scalaz that are relative to Validation.  The validation class can be found here:https://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/Validation.scala .  Also note that some methods come from MAB which can be found https://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/MAB.scala .

Assume the following classes and validations are defined:

  
  case class User(name: String)
  case class SecretCode
  

  def notEmpty: (String) => Validation[NonEmptyList[String], String] = {
    (s: String) => {
      if (s.size == 0) "must not be empty".wrapNel.fail
      else s.success
    }
  }

  def maxLength(max: Int): (String) => Validation[NonEmptyList[String], String] = {
    (s: String) => {
      if (s.size > max) ("The maximum length is" + max).wrapNel.fail
      else s.success
    }
  }

  def minLength(min: Int): (String) => Validation[String, String] = {
    (s: String) => {
      if (s.size < min) ("The minimum lenth is" + min).fail
      else s.success
    }
  }

  def checkAccess: (String) => Validation[NonEmptyList[String], SecretCode] = {
    (u: String) => {
      if (u == "Fred") "Fred is not allowed to know the Secret Code".wrapNel.fail
      else SecretCode().success
    }
  }

 

def fold[X](failure: E => X = identity[E] _, success: A => X = identity[A] _): X

Folds the validation into one value. This is a Catamorphism.

scala> notEmpty("").fold(f => None, s => User(s))
res3: Product with Serializable = None

scala> notEmpty("John").fold(f => None, s => User(s))
res4: Product with Serializable = User(John)

def map[B](f: A => B): Validation[E, B]
If this is a failure, this transformation will not be applied. Note that resulting success object has changes to whatever the result of the function f is, but the failure stays the same.

scala> notEmpty("").map(User(_))
res5: scalaz.Validation[scalaz.NonEmptyList[String],User] = Failure(NonEmptyList(must not be empty))

scala> notEmpty("John").map(User(_))
res6: scalaz.Validation[scalaz.NonEmptyList[String],User] = Success(User(John))

def foreach[U](f: A => U): Unit
If a Success, applies the function f to the success object. Otherwise does nothing.

scala> notEmpty("").foreach(x => println("Good Name:'" + x + "'") )

scala> notEmpty("John").foreach(x => println("Good Name:'" + x + "'") )
Good Name:'John'

def flatMap[EE >: E, B](f: A => Validation[EE, B]): Validation[EE, B]
If the current Validation is a Success, then flatMap performs a new validation specified by function f.
If the current Validation is a Failure, then flatMap wraps the failure object in the new Validation type. In both cases
the failure type basically stays the same, but the success type changes.

If there is a chain of flatmapped validations, the first failure with be the one reported and the subsequent validations will not run.

scala> notEmpty("").flatMap(checkAccess)
res15: scalaz.Validation[scalaz.NonEmptyList[String],SecretCode] = Failure(NonEmptyList(must not be empty))

scala> notEmpty("Fred").flatMap(checkAccess)
res16: scalaz.Validation[scalaz.NonEmptyList[String],SecretCode] = Failure(NonEmptyList(Fred is not allowed to know the Secret Code))

scala> notEmpty("John").flatMap(checkAccess)
res17: scalaz.Validation[scalaz.NonEmptyList[String],SecretCode] = Success(SecretCode())

 

def either : Either[E, A]

Turns the Validation[E,A] into an Either[E,A]

  

scala> notEmpty("").either match {
     | case Left(f) => "FAIL"
     | case Right(s) => "SUCCESS"
     | }
res7: java.lang.String = FAIL

scala> notEmpty("John").either match {
     | case Left(f) => "FAIL"
     | case Right(s) => "SUCCESS"
     | }
res8: java.lang.String = SUCCESS

def isSuccess : Boolean

Returns true if the Validation is a success object, false otherwise

  
scala> notEmpty("").isSuccess
res18: Boolean = false

scala> notEmpty("John").isSuccess
res19: Boolean = true

def isFailure : Boolean

Returns true if the Validation is a failure object, false otherwise.

  

scala> notEmpty("").isFailure
res20: Boolean = true

scala> notEmpty("John").isFailure
res21: Boolean = false

def toOption : Option[A]
If the Validation is a success, returns the success object wrapped in some. Otherwise it returns none.

  
scala> notEmpty("").toOption
res22: Option[String] = None

scala> notEmpty("Fred")
res23: scalaz.Validation[scalaz.NonEmptyList[String],String] = Success(Fred)

def >>*<<[EE >: E: Semigroup, AA >: A: Semigroup](x: Validation[EE, AA]): Validation[EE, AA]
If one of the two validations is a success, it returns the successful one. If both are failures it appends the failures together. If both are successes, the successes are appended to each other. A list may be a better example for concatenating a success result, but a NonEmptyList is a nice failure accumulator. Not that the append is done with the scalaz ? operator. TODO: find out where this is defined and elaborate.

scala> notEmpty("Fred") >>*<< notEmpty("John")
res24: scalaz.Validation[scalaz.NonEmptyList[String],String] = Success(FredJohn)

scala> notEmpty("Fred") >>*<< notEmpty("")
res25: scalaz.Validation[scalaz.NonEmptyList[String],String] = Success(Fred)

scala> notEmpty("") >>*<< notEmpty("John")
res26: scalaz.Validation[scalaz.NonEmptyList[String],String] = Success(John)

scala> notEmpty("") >>*<< notEmpty("")
res27: scalaz.Validation[scalaz.NonEmptyList[String],String] = Failure(NonEmptyList(must not be empty, must not be empty))

def fail : FailProjection[E, A] = new FailProjection[E, A]
Use map (defined in MA) to convert a Failure from one type to another.

def lift[M[_]: Pure, AA >: A]: Validation[E, M[AA]]
If the validation is a success, it wraps the success object into the specified container. Lifting to a List or Option seems useful, but I'm not sure what other options there are.

scala> notEmpty("John").lift[Option, String]
res32: scalaz.Validation[scalaz.NonEmptyList[String],Option[String]] = Success(Some(John))

scala> notEmpty("").lift[Option, String]
res33: scalaz.Validation[scalaz.NonEmptyList[String],Option[String]] = Failure(NonEmptyList(must not be empty))

scala> notEmpty("John").lift[List, String]
res34: scalaz.Validation[scalaz.NonEmptyList[String],List[String]] = Success(List(John))

def liftFailNel: Validation[NonEmptyList[E], A] = fail.liftNel
Wraps the failure value in a NonEmptyList. I tend to define my return type to be Validation[NonEmptyList[E], A], however if you defined a return type to be Validation[E,A] you could wrap the result in liftFailNel to have the same type.


scala> val five = minLength(5)
five: String => scalaz.Validation[String,String] = 

scala> five("1234").liftFailNel
res35: scalaz.Validation[scalaz.NonEmptyList[String],String] = Failure(NonEmptyList(The minimum lenth is5))

scala> five("123456").liftFailNel
res36: scalaz.Validation[scalaz.NonEmptyList[String],String] = Success(123456)

def |||[AA >: A](f: E => AA): AA
Like getOrElse on option, but the default value takes an argument. If success, returns the success object. If failure, uses the specified function to produce a value.


scala> notEmpty("John") ||| {x => "Anonymous: where failure was " + x.list.mkString}
res50: String = John

scala> notEmpty("") ||| {x => "Anonymous: where failure was " + x.list.mkString}
res51: String = Anonymous: where failure was must not be empty

def |[AA >: A](f: => AA): AA
Like getOrElse, provide a default value if failure.

scala> notEmpty("John") | User("Anonymous")
res42: java.io.Serializable = John

scala> notEmpty("") | User("Anonymous")
res43: java.io.Serializable = User(Anonymous)

def forall(f: A => Boolean): Boolean
Checks to see if the conditional is true for the success object. If validation is a Failure, it is considered true.

Defined of MAB

def :->[D](g: B => D)(implicit b: Bifunctor[M]): M[A, D]
This is essentially the same thing as map.

scala> notEmpty("") :-> {User(_)}
res61: scalaz.Validation[scalaz.NonEmptyList[String],User] = Failure(NonEmptyList(must not be empty))

scala> notEmpty("Fred") :-> {User(_)}
res62: scalaz.Validation[scalaz.NonEmptyList[String],User] = Success(User(Fred))

def <-:[C](f: A => C)(implicit b: Bifunctor[M]): M[C, B]

This is essentially the same as ||| but with different syntax.

scala> {x: NonEmptyList[String] => x.list.mkString} <-: notEmpty("")
res68: scalaz.Validation[String,String] = Failure(must not be empty)

scala> {x: NonEmptyList[String] => x.list.mkString} <-: notEmpty("Fred")
res69: scalaz.Validation[String,String] = Success(Fred)

Defined on ApplicativeBuilder

def |@|[C](c: M[C])
Allows one to chain validation, collecting all the failures. To do this you can apply the result to a partial function. If there is at least one failure, then the result will be a collection of all the failures.

 
 

Arrow[Function1]

On August 2, 2011, in Scala, scalaz, by OleTraveler

Scalaz defines Arrow as such.

trait Arrow[A[_, _]] {

  val category: Category[A]
  def arrow[B, C](f: B => C): A[B, C]
  def first[B, C, D](a: A[B, C]): A[(B, D), (C, D)]
  def second[B, C, D](a: A[B, C]): A[(D, B), (D, C)]
}

Easy to see with Function1 implementation.

  implicit def Function1Arrow: Arrow[Function1] = new Arrow[Function1] {
    val category = Category.Function1Category
    
    def arrow[B, C](f: B => C) = f

    /** takes a Function1[B,C] and returns a Function1[(B,D),(C,D)].  As you can see
    * only the _1, or the 'first' argument is modified to a different type
    * and the _2 or 'second' is not touched.
    */
    def first[B, C, D](a: B => C) =
      (bd: (B, D)) => (a(bd._1), bd._2)

    /** takes a Function1[B,C] and returns a Function1[(D,B),(D,C)].  
    * Only the _2 or second element of the Tuple is touched.
    */
    def second[B, C, D](a: B => C) =
      (db: (D, B)) => (db._1, a(db._2))
  }

And here we have some example uses.

scala> val plus: Function1[Int,Int] = (_:Int) + 1 
plus: (Int) => Int = <function1>

scala> val firstChar: Function1[String,Option[Char]] = (s:String) => Option(s.charAt(0))
firstChar: (String) => Option[Char] = <function1>

scala>  val m2 = Map(1 -> "astroman", 2 -> "boat")
m2: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,astroman), (2,boat))

scala>  m2.map(plus.first)
res37: scala.collection.immutable.Map[Int,java.lang.String] = Map((2,astroman), (3,boat))

scala> m2.map(firstChar.second)                                                          
res39: scala.collection.immutable.Map[Int,Option[Char]] = Map((1,Some(a)), (2,Some(b)))

The trait MAB defines some useful functions for composing functions.

The *** will create a function in which each composted function will work on individual values of the Tupl2.

scala> val p = plus *** firstChar
p: ((Int, String)) => (Int, Option[Char]) = <function>

scala> m2.map(p)
res41: scala.collection.immutable.Map[Int,Option[Char]] = Map((2,Some(a)), (3,Some(b)))

&&& will perform two operations on a single value and put the values in a Tuple2.

scala> val plus2 = (_:Int) + 2
plus2: (Int) => Int = <function1>

scala> val q = plus &&& plus2
q: (Int) => (Int, Int) = <function1>

scala> q(3)
res44: (Int, Int) = (4,5)

The product method will perform the same operation on both elements of the Tuple2

scala> plus.product apply (1 -> 2)
res46: (Int, Int) = (2,3)
 

The way hibernate implements lazy loading is that a proxy object facilitates the loading of them. This is well know and is documented here:

http://community.jboss.org/wiki/ProxyVisitorPattern

Unfortunately this means that pattern matching on classes may now work. For instance, if the following were hibernate entities:

trait PaymentSource
class CreditCard extends PaymentSource
class Check extends PaymentSource
class User {
  val paymentSources = List[PaymentSource]
}

Doing a patter match as such would not work if the loaded object is a proxy:

user.paymentSources.map( _ match {
  case _:CreditCard => "Credit Card"
  case _:Check => "Check"
})

In Java, the work around is to use a visitor pattern.  Here it is implemented in scala:

trait PaymentSource {
  def accept[T](visitor: PsVisitor[T]) : T
}

class CreditCard extends PaymentSource{
  override def accept[T](visitor: PsVisitor[T]) = {
    visitor.visit(this)
  }
}

class Check extends PaymentSource{
  override def accept[T](visitor: PsVisitor[T]) = {
    visitor.visit(this)
  }
}

trait PsVisitor[T] {
  def visit(cc: CreditCard) : T
  def visit(ck: Check) : T
}

Although this works consistently, the syntax is not quite as nice.

  user.paymentSources.map( _.accept(new PsVisitor[Unit]() {
        def visit(cc: CreditCard) = println("CreditCard")
        def visit(ck: Check) = println("Check")
   })

A solution to this is to add simple wrapper classes for each concrete class and a simple visitor implementation to do the actual wrapping

object PsFilter {
  def apply(ps: PaymentSource) = {
    ps.accept(new PsVisitor[PsResult] {
      def visit(cc: CreditCard) = CreditCardResult(cc)
      def visit(ck: Check) = CheckResult(ck)
    })
  }
}

sealed trait PsResult
case class CreditCardResult(cc: CreditCard) extends PsResult
case class CheckResult(cc: Check) extends PsResult

And now we can pattern match again

user.paymentSources.map(PsFilter(_) match {
      case CreditCardResult(cc) => "CreditCard"
      case CheckResult(ck) => "Check"
})

Source for this can be found at Git Hub: https://github.com/OleTraveler/HibernateInheritance

 

Tagged with:  

Old Blog

On April 20, 2011, in Uncategorized, by Ole Traveler

My old blog is here: http://defprattle.blogspot.com/