您的代码几乎已经在编译- 唯一的问题是fPerson
类型(String, Int) => Person
而不是((String, Int)) => Person
(采用元组而不是 2 个单独的参数)。
下面的解决方案并不好,尽管它可能对 TupleXXL 更有效。这是带有类型类(Scastie)的更好版本:
val fPerson: ((String, Int)) => Person = Person.apply _
opaque type Extract[GT <: Tuple, RT <: Tuple] = GT => RT
given Extract[EmptyTuple, EmptyTuple] = Predef.identity
given [A, PG <: Tuple, PR <: Tuple](using p: Extract[PG, PR])
as Extract[Get[A] *: PG, A *: PR] = {
case h *: t => h.get *: p(t)
}
def genericF[GT <: Tuple, RT <: Tuple, B](
f: RT => B,
t: GT
)(using extract: Extract[GT, RT]): Get[B] = Put(f(extract(t)))
这是您可以使用实现的一种方法genericF
(Tuple.InverseMap
请注意,我将两个参数切换为genericF
:
val fPerson: ((String, Int)) => Person = Person.apply _
type ExtractG = [G] =>> G match {
case Get[a] => a
}
type AllGs[T <: Tuple] = T match {
case EmptyTuple => DummyImplicit
case Get[_] *: t => AllGs[t]
case _ => Nothing
}
def extract[T <: Tuple](t: T)(using AllGs[T]): Tuple.InverseMap[T, Get] =
t.map {
[G] => (g: G) => g.asInstanceOf[Get[_]].get.asInstanceOf[ExtractG[G]]
}.asInstanceOf[Tuple.InverseMap[T, Get]]
def genericF[B](
t: Tuple,
f: Tuple.InverseMap[t.type, Get] => B
)(using AllGs[t.type]): Get[B] = Put(f(extract(t)))
val person: Get[Person] = genericF(t, fPerson)
ExtractG
是进行PolyFunction
编译,因为它要求您将类型构造函数应用于其类型参数。
AllGs
是验证元组仅由Get
s 组成,因为正如 Dmytro Mitin 所指出的,否则它不是类型安全的。如果都是Get
s,那么类型就变成DummyImplicit
Scala 为我们提供的。否则就是Nothing
. 我猜它可能与范围内的其他隐式/给定Nothing
的 s 冲突,但如果你已经有一个,那么无论如何你都搞砸了。
请注意,这仅在您拥有Get
并且如果您还希望它适用于像(Put[String], GetSubclass[Int])
.
OP 的 Travis Stevens 已经设法AllGs
通过使用IsMappedBy
. 这就是他们得到的(Scastie):
val fPerson: ((String, Int)) => Person = Person.apply _
type ExtractG = [G] =>> G match {
case Get[a] => a
}
def extract[T <: Tuple, I <: Tuple.InverseMap[T, Get]](
t: T
)(using Tuple.IsMappedBy[Get][T]): I =
t.map {
[G] => (g: G) => g.asInstanceOf[Get[_]].get.asInstanceOf[ExtractG[G]]
}.asInstanceOf[I]
def genericF[T <: Tuple, I <: Tuple.InverseMap[T, Get], B](
t: T,
f: I => B
)(using Tuple.IsMappedBy[Get][T]): Get[B] = Put(f(extract(t)))
这里有一个使用依赖类型,只是为了好玩(Scastie):
type Extract[T <: Tuple] <: Tuple = T match {
case EmptyTuple => EmptyTuple
case Get[a] *: t => a *: Extract[t]
}
type AllGs[T <: Tuple] = T match {
case EmptyTuple => DummyImplicit
case Get[_] *: t => AllGs[t]
case _ => Nothing
}
def genericF[T <: Tuple : AllGs, B](
t: T,
f: Extract[t.type] => B
): Get[B] = {
def extract[T <: Tuple](t: T): Extract[T] = t match {
case _: EmptyTuple => EmptyTuple
case (head *: tail): (Get[_] *: _) => head.get *: extract(tail)
}
Put(f(extract(t)))
}
我希望Extract
不会为像这样的元组编译(Put("foo"), 3)
,但不幸的AllGs
是,仍然是必要的。