Luke Cycon
Luke Cycon

Reputation: 648

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?

Upvotes: 6

Views: 744

Answers (1)

Travis Brown
Travis Brown

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

Related Questions