我的直觉告诉我,在一般情况下,没有任何宏观或复杂类型的体操可以解决这个问题。 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-Tuples的一般解决方案。提出的答案专门针对2元组。
我是不是写了一个宏,还是可以将Shapeless / Scalaz保存一天?
答案 0 :(得分:5)
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(或者更确切地说是甚至更多类型级版本)具有静态类型为Some
或None
的元素的元组,并获得静态类型为Some
或None
的结果。这可能在某些情况下有应用,但一般情况下,如果你有一个静态输入Some[A]
的东西,你应该只将其表示为A
,我猜你可能想要更少的类型级版本。
请注意,shapeless-contrib Sequencer
适用于任何应用仿函数,而不仅仅是Option
,这意味着您可以轻松地重写我的foo
以获取{{1}键入参数并返回F[_]: Applicative
。您也可以使用仅在F[T]
上运行的自己不那么通用的版本,并且实现可能比shapeless-contrib更简单。