Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

N-Tuple of Options to Option of N-Tuple

My gut tells me that nothing short of macros or complex type gymnastics can solve this question in the general case. Can Shapeless or Scalaz possibly help me here? Here is a specific instantiation of the problem with N=2, but the solution I'm looking for would hold for all reasonable values of N:

foo((Some(1), Some("bar"))) == Some((1, "bar"))
foo((None, Some("bar"))) == None
foo((Some(1), None)) == None

Again, this is not a duplicate of this question, as I am looking for the general solution for N-Tuples. Answers posed there are specialized to 2-Tuples.

Am I stuck writing a macro, or can Shapeless/Scalaz save the day?

like image 557
Luke Cycon Avatar asked May 13 '15 05:05

Luke Cycon


1 Answers

shapeless-contrib makes this pretty easy:

import shapeless._, ops.hlist.Tupler, contrib.scalaz._, scalaz._, Scalaz._

def foo[T, L <: HList, O <: HList](t: T)(implicit
  gen: Generic.Aux[T, L],
  seq: Sequencer.Aux[L, Option[O]],
  tup: Tupler[O]
): Option[tup.Out] = seq(gen.to(t)).map(tup(_))

This requires the elements in the argument to be statically typed as Option:

scala> foo((some(1), some("bar")))
res0: Option[(Int, String)] = Some((1,bar))

scala> foo((none[Int], some("bar")))
res1: Option[(Int, String)] = None

scala> foo((some(1), none[String]))
res2: Option[(Int, String)] = None

As Alexandre Archambault mentioned on Gitter, it's also possible to write a type-level version (or rather an even more type-level version, I guess) where you take a tuple with elements that are statically typed as Some or None and get a result that is statically typed as a Some or None. This may have applications in some situations, but in general if you've got something statically typed as a Some[A] you should just represent it as an A, and I'm guessing you probably want the less type-level version.

Note that shapeless-contrib's Sequencer works on any applicative functor, not just Option, which means you could pretty easily rewrite my foo to take a F[_]: Applicative type parameter and return an F[T]. You could also roll your own less generic version that only worked on Option, and the implementation would probably be a little simpler than the one in shapeless-contrib.

like image 120
Travis Brown Avatar answered Nov 05 '22 22:11

Travis Brown