Skip to content

Instantly share code, notes, and snippets.

@sam
Created September 18, 2013 22:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sam/6616857 to your computer and use it in GitHub Desktop.
Save sam/6616857 to your computer and use it in GitHub Desktop.
Scalaz's Applicative Functor for Futures.
// Zip together a couple Futures, including one that returns an Option
// and pass them to a function to give me a new object:
api.channels.tree zip api.pages.getByRoute(route) map {
case (tree, Some(page)) => Some(new PagePresenter(context, tree, page))
case _ => None
}
// Now with an Applicative Functor!
(api.channels.tree |@| api.pages.getByRoute(route)) {
case (tree, Some(page)) => Some(new PagePresenter(context, tree, page))
case _ => None
}
// Note the Partial Functions above are the same:
//
// (Channel, Option[Page]) => Option[PagePresenter]
// Ok, whatever. We saved a few characters. But wait! There's more!
api.channels.tree zip api.photos.all zip api.photos.getBySlug(slug)) map {
case ((tree, photos), Some(photo)) => Some(new PhotoPresenter(context, tree, photos, photo))
case _ => None
}
// And the Applicative Functor version now:
(api.channels.tree |@| api.photos.all |@| api.photos.getBySlug(slug)) {
case (tree, photos, Some(photo)) => Some(new PhotoPresenter(context, tree, photos, photo))
case _ => None
}
// But now we see something else. The zipped version says: zip[A,B]. So after the
// first zip you get Future[(A,B)]. Cool. But on the second you get Future[((A,B),C)]
// since you're calling zip[(A,B), C] on Future[(A,B)]! Which means our Partial Function
// is now:
//
// ((Channel, Seq[Photo]), Option[Photo]) => Option[PhotoPresenter]
//
// Instead of the prettier version the Applicative gets to use:
//
// (Channel, Seq[Photo], Option[Photo]) => Option[PhotoPresenter]
//
// Visually I'll admit it's not the end of the world. A few extra parenthesis. But think
// about what it actually *means*. Especially since you're working with Tuples.
//
// Zipped: Tuple2[Tuple2[Channel, Seq[Photo]], Option[Photo]]
//
// Applicative: Tuple3[Channel, Seq[Photo], Option[Photo]]
//
// Now doesn't that just look better?
// But let's go deeper...
(api.channels.tree |@| api.channels.getBySlug(slug) |@|
api.releases.latest(20) |@| api.releases.count |@|
api.photos.latest(20) |@| api.photos.count |@|
api.videos.latest(20) |@| api.videos.count) {
case (tree, Some(channel), releases, releaseCount, photos, photoCount, videos, videoCount) =>
Some(new ChannelPresenter(context, tree, channel, releases, releaseCount, photos, photoCount, videos, videoCount))
case _ => None
}
// So that's a...
//
// Tuple8[Channel, Option[Channel], Seq[Release], Int, Seq[Photo], Int, Seq[Video], Int]
//
// Not bad...
// Can you imagine the zipped version? No? Well let's try it:
api.channels.tree zip api.channels.getBySlug(slug) zip
api.releases.latest(20) zip api.releases.count zip
api.photos.latest(20) zip api.photos.count zip
api.videos.latest(20) zip api.videos.count map {
case (((((((tree, Some(channel)), releases), releaseCount), photos), photoCount), videos), videoCount) =>
Some(new ChannelPresenter(context, tree, channel, releases, releaseCount, photos, photoCount, videos, videoCount))
case _ => None
}
// I've tried writing the signature out, but it's too wide and I get confused. Going to nest this one:
//
// Tuple2[
// Tuple2[
// Tuple2[
// Tuple2[
// Tuple2[
// Tuple2[
// Tuple2[
// Channel,
// Option[Channel]],
// Seq[Release]],
// Int],
// Seq[Photo]],
// Int],
// Seq[Video]],
// Int]
//
// Uhmm... I think that's right. Maybe. Eww.
// Anyways, scalaz. Applicative Functors. Cool.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment