使用值与参数顺序不匹配的无形 HList 调用 Scala Function2
Invoke a Scala Function2 with a shapeless HList whose values do not match the argument order
我想构建相当于:
def applyWithHList2[A1, A2, R, L <: HList](l: L, f: Function2[A1, A2, R]): Try[R]
列表中的值是这样的,在 N 中选择 l.unify
的 2 个可能的值组合,最多有一个可用于调用函数。没有其他可用的类型信息。
如果没有办法调用函数,结果应该是Failure
和MatchError
。否则,结果应该是 Try(f(a1, a2))
.
我仍然习惯于无形,非常感谢有关如何解决此问题的建议。
有趣的是,如果 HList
:
中没有适当类型的元素,那么编写一个无法编译的版本要容易得多
import shapeless._, ops.hlist.Selector
def applyWithHList2[A1, A2, R, L <: HList](l: L, f: (A1, A2) => R)(implicit
selA1: Selector[L, A1],
selA2: Selector[L, A2]
): R = f(selA1(l), selA2(l))
如果你真的想要一个运行时错误(在 Try
中)对于没有适用对的情况,你可以使用默认的 null
实例技巧:
import scala.util.{ Failure, Success, Try }
def applyWithHList2[A1, A2, R, L <: HList](l: L, f: (A1, A2) => R)(implicit
selA1: Selector[L, A1] = null,
selA2: Selector[L, A2] = null
): Try[R] = Option(selA1).flatMap(s1 =>
Option(selA2).map(s2 => f(s1(l), s2(l)))
).fold[Try[R]](Failure(new MatchError()))(Success(_))
如果您觉得不愉快(确实如此),您可以使用自定义类型 class:
trait MaybeSelect2[L <: HList, A, B] {
def apply(l: L): Try[(A, B)] = (
for { a <- maybeA(l); b <- maybeB(l) } yield (a, b)
).fold[Try[(A, B)]](Failure(new MatchError()))(Success(_))
def maybeA(l: L): Option[A]
def maybeB(l: L): Option[B]
}
object MaybeSelect2 extends LowPriorityMaybeSelect2 {
implicit def hnilMaybeSelect[A, B]: MaybeSelect2[HNil, A, B] =
new MaybeSelect2[HNil, A, B] {
def maybeA(l: HNil): Option[A] = None
def maybeB(l: HNil): Option[B] = None
}
implicit def hconsMaybeSelect0[H, T <: HList, A](implicit
tms: MaybeSelect2[T, A, H]
): MaybeSelect2[H :: T, A, H] = new MaybeSelect2[H :: T, A, H] {
def maybeA(l: H :: T): Option[A] = tms.maybeA(l.tail)
def maybeB(l: H :: T): Option[H] = Some(l.head)
}
implicit def hconsMaybeSelect1[H, T <: HList, B](implicit
tms: MaybeSelect2[T, H, B]
): MaybeSelect2[H :: T, H, B] = new MaybeSelect2[H :: T, H, B] {
def maybeA(l: H :: T): Option[H] = Some(l.head)
def maybeB(l: H :: T): Option[B] = tms.maybeB(l.tail)
}
}
trait LowPriorityMaybeSelect2 {
implicit def hconsMaybeSelect2[H, T <: HList, A, B](implicit
tms: MaybeSelect2[T, A, B]
): MaybeSelect2[H :: T, A, B] = new MaybeSelect2[H :: T, A, B] {
def maybeA(l: H :: T): Option[A] = tms.maybeA(l.tail)
def maybeB(l: H :: T): Option[B] = tms.maybeB(l.tail)
}
}
然后:
def applyWithHList2[A1, A2, R, L <: HList](l: L, f: (A1, A2) => R)(implicit
ms2: MaybeSelect2[L, A1, A2]
): Try[R] = ms2(l).map(Function.tupled(f))
但是仅仅为了丢掉一些 compile-time 安全性就需要做很多工作。
请注意,这些方法中的 none 强制执行约束,即 HList
中最多只有一对元素可以应用该函数,因为我将其读作 pre-condition 在你的问题中。绝对有可能编写一个在编译时强制执行约束的解决方案(甚至可能比上面的 MaybeSelect2
实现更短)。
我想构建相当于:
def applyWithHList2[A1, A2, R, L <: HList](l: L, f: Function2[A1, A2, R]): Try[R]
列表中的值是这样的,在 N 中选择 l.unify
的 2 个可能的值组合,最多有一个可用于调用函数。没有其他可用的类型信息。
如果没有办法调用函数,结果应该是Failure
和MatchError
。否则,结果应该是 Try(f(a1, a2))
.
我仍然习惯于无形,非常感谢有关如何解决此问题的建议。
有趣的是,如果 HList
:
import shapeless._, ops.hlist.Selector
def applyWithHList2[A1, A2, R, L <: HList](l: L, f: (A1, A2) => R)(implicit
selA1: Selector[L, A1],
selA2: Selector[L, A2]
): R = f(selA1(l), selA2(l))
如果你真的想要一个运行时错误(在 Try
中)对于没有适用对的情况,你可以使用默认的 null
实例技巧:
import scala.util.{ Failure, Success, Try }
def applyWithHList2[A1, A2, R, L <: HList](l: L, f: (A1, A2) => R)(implicit
selA1: Selector[L, A1] = null,
selA2: Selector[L, A2] = null
): Try[R] = Option(selA1).flatMap(s1 =>
Option(selA2).map(s2 => f(s1(l), s2(l)))
).fold[Try[R]](Failure(new MatchError()))(Success(_))
如果您觉得不愉快(确实如此),您可以使用自定义类型 class:
trait MaybeSelect2[L <: HList, A, B] {
def apply(l: L): Try[(A, B)] = (
for { a <- maybeA(l); b <- maybeB(l) } yield (a, b)
).fold[Try[(A, B)]](Failure(new MatchError()))(Success(_))
def maybeA(l: L): Option[A]
def maybeB(l: L): Option[B]
}
object MaybeSelect2 extends LowPriorityMaybeSelect2 {
implicit def hnilMaybeSelect[A, B]: MaybeSelect2[HNil, A, B] =
new MaybeSelect2[HNil, A, B] {
def maybeA(l: HNil): Option[A] = None
def maybeB(l: HNil): Option[B] = None
}
implicit def hconsMaybeSelect0[H, T <: HList, A](implicit
tms: MaybeSelect2[T, A, H]
): MaybeSelect2[H :: T, A, H] = new MaybeSelect2[H :: T, A, H] {
def maybeA(l: H :: T): Option[A] = tms.maybeA(l.tail)
def maybeB(l: H :: T): Option[H] = Some(l.head)
}
implicit def hconsMaybeSelect1[H, T <: HList, B](implicit
tms: MaybeSelect2[T, H, B]
): MaybeSelect2[H :: T, H, B] = new MaybeSelect2[H :: T, H, B] {
def maybeA(l: H :: T): Option[H] = Some(l.head)
def maybeB(l: H :: T): Option[B] = tms.maybeB(l.tail)
}
}
trait LowPriorityMaybeSelect2 {
implicit def hconsMaybeSelect2[H, T <: HList, A, B](implicit
tms: MaybeSelect2[T, A, B]
): MaybeSelect2[H :: T, A, B] = new MaybeSelect2[H :: T, A, B] {
def maybeA(l: H :: T): Option[A] = tms.maybeA(l.tail)
def maybeB(l: H :: T): Option[B] = tms.maybeB(l.tail)
}
}
然后:
def applyWithHList2[A1, A2, R, L <: HList](l: L, f: (A1, A2) => R)(implicit
ms2: MaybeSelect2[L, A1, A2]
): Try[R] = ms2(l).map(Function.tupled(f))
但是仅仅为了丢掉一些 compile-time 安全性就需要做很多工作。
请注意,这些方法中的 none 强制执行约束,即 HList
中最多只有一对元素可以应用该函数,因为我将其读作 pre-condition 在你的问题中。绝对有可能编写一个在编译时强制执行约束的解决方案(甚至可能比上面的 MaybeSelect2
实现更短)。