That moment when...

The moment had to arrive eventually. You know, that moment when after months and years learning Functional Programming (FP), you realize and understand why you would need Heterogeneous Lists (HLists) and why Shapeless is a great Scala library.

It all started at work, with the very basic task of validating several parameters in a REST endpoint. To give more context, this service was written on top of the popular Spring framework, in Java, with a bit of help from Javaslang. Validating the parameter resulted in obtaining an object of type Validation<ErrorOn<T>, T>> where for some parameters T was String or Integer or Double. Yet with (standard) Java, this is impossible :

Validation<ErrorOn<String>, String> field0 = someFn(...);
Validation<ErrorOn<Integer>, Integer> field1 = someOtherFn(...);
Validation<ErrorOn<Double>, Double> field2 = lastValidFn(...); 
List<???> validations = List(field0, field1, field2)

The ugly trick is often to find the common upper class or interface of all instance possible, and accepting to completely loose track of the parametric type of the ErrorOn<>... Yet, in Scala a much more fun solution preserve the type safety, and it is using the Shapeless library.

Here is how :

import shapeless.{HNil, Poly1}


object ShapelessDemo extends App {

  object toErrorMessage extends Poly1 {
    implicit def caseString = at[Either[ErrorOn[String], String]](i => i.left.map(e => s"$e is a String"))
    implicit def caseInt = at[Either[ErrorOn[Int], String]](i => i.left.map(e => s" $e is an Int !" ))
  }

  case class ErrorOn[T](a: T, message: String)

  val error0: Either[ErrorOn[String], String] = Left(ErrorOn("wrong string", "invalid value"))
  val error1: Either[ErrorOn[Int], String] = Left(ErrorOn(3, "not enough"))

  val validations = error0 :: error1 :: HNil

  val res = validations map toErrorMessage

  println(res)

}

This is a very basic example, that probably scalaz Validation solves in other ways, may be the initial problem is not correctly modeled and the ErrorOn type is poorly designed but this is a big win for me. The perks of this approach is that everything is checked at compile time which means that deleting the caseInt implicit function will produce a compile error.

I guess I am far from being a type expert (as a matter of fact, I feel more like a young beginner ), but this case made me smile and above all, made me want to learn more.