I’m learning scala, play, and web services all at once so bear with me. I’ve set up a little aggregator service that combines a weather web service and google’s geocode and places web services. I’ve got something working but I’m a little confused on the proper way to handle errors. (I posted the code at the end of the post)
So the places api uses lat/long and so I use the geocode api to get lat/long from the zip code. When processing the response from the call to the geocode api I end up with an (Option[String], Option[String]) (held in the maybeLoc val). Inside the match statement that checks maybeLoc, if it ends up being (None, None), I return Promise() because I need to return a Promise from the flatmap call.
I have two questions about this:
1.) What is the right way to handle the case of not being able to do any further processing while inside one of these flatMap or map calls? It requires me to return a promise, but making an empty Promise that will just time out when I go to redeem it seems like a really bad idea.
2.) Am I right in assuming that the call to Promise() makes an empty promise object that will always time out when trying to redeem it? I couldn’t really tell from the scaladoc and couldn’t find anything about it from google.
I hope my questions make sense to you and are clear enough. Here is the code:
def bothAsJson(zipcode:String) = Action {
val promiseOfLoc = Geocode.buildUrlFor(zipcode).get()
val promiseOfWeather = Weather.buildUrlFor(zipcode, "json").get()
val result = promiseOfLoc.flatMap { locResp =>
val maybeLoc = Geocode.extractLocation(locResp.body.toString())
maybeLoc match {
case (Some(lat), Some(lng)) => {
val promiseOfPlaces = Places.buildUrlFor(lat,lng).get()
promiseOfPlaces.flatMap { placesResp =>
promiseOfWeather.map { weatherResp =>
(weatherResp.body.toString(), placesResp.body.toString())
}
}
}
case _ => Promise()
}
}
Async {
result.orTimeout("Timeout!", 2000).map {response =>
response.fold(
result => Ok("Got:\n\nweather:\n" + result._1 + "\n\nplaces:\n" + result._2),
timeout => InternalServerError(timeout)
)
}
}
}
If you get (None, None) you shouldn’t be looking to timeout, but return another error message I believe. I’ve provided an example below.
I think you need OptionT from from scalaz 7. I would write this as:
With
OptionTwe can flatMap as if it was anOption, gaining the ability not to process anything if we get a None. At the end we are left with aPromise[Option[T]]which is very nice for this. Another way good way to handle errors is to use Either/EtherT with the same method.|@|is an Applicative Builder. It takes 2 Options, and returning anOption((Int, Int))if both sides areSome. If one side or both sides areNone, it returns aNone.Note for this to work you will need a scalaz
Monad[Promise]instanceAlso please note that I have written all this code in the SO editor, There could be braces missing. But all the code should be more or less right, I tested parts of it in the repl.
Please feel free to ask for help on #scalaz on freenode irc, or on the scalaz google groups.