Background
I have Map[String,String]
of configuration values. I want to extract a series of keys and provide meaningful error messages if any of them are missing. For example:
val a = Map("url"->"http://example.com", "user"->"bob", "password"->"12345")
Say I want to transform this into a case class:
case class HttpConnectionParams(url:String, user:String, password: String)
Now, I can simply use a for loop to extract the values:
for(url <- a.get("url");
user <- a.get("user");
password <- a.get("password")) yield {
HttpConnectionParams(url,user,password)
}
To get an Option[HttpConnectionParams]
. This is nice and clean, except if I get a None
then I don't know what was missing. I'd like to provide that information.
Validation with Scalaz
Enter scalaz. I'm using version 7.1.3.
From what I've been able to put together (a good reference is here) I can use disjunctions:
for(url <- a.get("url") \/> "Url must be supplied";
user <- a.get("user") \/> "Username must be supplied";
password <- a.get("password") \/> "Password must be supplied") yield {
HttpConnectionParams(url,user,password)
}
This is nice because now I get an error message, but this is railway oriented because it stops at the first failure. What if I want to get all of the errors? Let's use validation and the applicative builder (aka "|@|"):
val result = a.get("url").toSuccess("Url must be supplied") |@|
a.get("username").toSuccess("Username must be supplied") |@|
a.get("password").toSuccess("Password must be supplied")
result.tupled match {
case Success((url,user,password)) => HttpConnectionParams(url,user,password)
case Failure(m) => println("There was a failure"+m)
}
Questions
This does what I expect, but I have some questions about the usage:
- Is there an easy to use alternative to scalaz for this use-case? I'd prefer to not open pandora's box and introduce scalaz if I don't have to.
- One reason I'd like to not use scalaz is that it's really really hard to figure out what to do if you don't, like me, know the entire framework. For example, what is the list of implicits that you need to get the above code to work?
import scalaz._
somehow didn't work for me.[1] How can I figure this out from the API docs? - Is there a more succinct way to express the validation use-case? I stumbled my way through until I arrived at something that worked and I have no idea if there are other, better ways of doing the same thing in scalaz.
[1] After much consternation I arrived at this set of imports for the applicative use-case. Hopefully this helps somebody:
import scalaz.std.string._
import scalaz.syntax.std.option._
import scalaz.syntax.apply._
import scalaz.Success
import scalaz.Failure
import scalaz._, Scalaz._
. I tried little-s scalaz and then when that didn't fix everything, I tried big-s scalaz by itself thinking it was a package. When that didn't work I went on an import hunt. More information on imports with scalaz here: eed3si9n.com/learning-scalaz/day13.html – Clevelandclevenger