Reputation: 648
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?
Upvotes: 6
Views: 744
Reputation: 139038
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.
Upvotes: 6