N元组选项到N元组选项

N-Tuple of Options to Option of N-Tuple

我的直觉告诉我,在一般情况下,只有宏或复杂类型体操才能解决这个问题。 Shapeless 或 Scalaz 可以帮助我吗?这是 N=2 问题的具体实例化,但我正在寻找的解决方案适用于 N:

的所有合理值
foo((Some(1), Some("bar"))) == Some((1, "bar"))
foo((None, Some("bar"))) == None
foo((Some(1), None)) == None

同样,这不是 this question 的副本,因为我正在寻找 N 元组的通用解决方案。那里提出的答案专门针对 2 元组。

我是不是写宏卡住了,还是 Shapeless/Scalaz 可以挽救局面?

shapeless-contrib 让这变得非常简单:

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(_))

这需要将参数中的元素静态类型化为 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

正如 Alexandre Archambault 在 Gitter 上提到的那样,也可以编写一个 type-level version(或者我想 甚至更多 类型级别的版本)具有静态类型为 SomeNone 的元素的元组,并获得静态类型为 SomeNone 的结果。这在某些情况下可能有应用,但一般来说,如果你有一些静态类型为 Some[A] 的东西,你应该只将它表示为 A,我猜你可能想要 less 类型-级版本。

请注意,shapeless-contrib 的 Sequencer 适用于任何应用仿函数,而不仅仅是 Option,这意味着您可以很容易地重写我的 foo 以采用 F[_]: Applicative类型参数和 return 和 F[T]。你也可以推出你自己的不太通用的版本,它只适用于 Option,并且实现可能比 shapeless-contrib 中的那个更简单一些。