Scala 3-提取一阶类型上包装器和InverseMap的元组

Scala 3-提取一阶类型上包装器和InverseMap的元组,scala,tuples,dotty,scala-3,Scala,Tuples,Dotty,Scala 3,我正在尝试创建一个函数,它接受一个高级类型的元组,并将一个函数应用于高级类型中的类型 在下面的例子中,有一个trait Get[a],它是我们的高级类型。还有一个Get的元组:(Get[String],Get[Int])以及函数from(String,Int)=>Person Scala-3有一个名为InverseMap的匹配类型,它将类型(Get[String],Get[Int])转换为本质上的类型(String,Int) 因此,最终的目标是编写一个函数,它可以接受一个具有任意数量的Get[\

我正在尝试创建一个函数,它接受一个高级类型的元组,并将一个函数应用于高级类型中的类型

在下面的例子中,有一个
trait Get[a]
,它是我们的高级类型。还有一个Get的元组:
(Get[String],Get[Int])
以及函数from
(String,Int)=>Person

Scala-3有一个名为InverseMap的匹配类型,它将类型(Get[String],Get[Int])转换为本质上的类型(String,Int)

因此,最终的目标是编写一个函数,它可以接受一个具有任意数量的
Get[\u]
类型的元组和一个输入与InserveMap类型匹配的函数,并最终返回一个
Get[\u]
,其中包装的类型是该函数的结果

我尝试在下面创建一个名为
genericF
的函数来显示所需的行为,尽管它可能不正确——但我认为它至少显示了正确的意图

  case class Person(name: String, age: Int)
  trait Get[A] {
    def get: A
  }
  case class Put[A](get: A) extends Get[A]
    
  val t: (Get[String], Get[Int]) = (Put("Bob"), Put(42))
  
  val fPerson: (String,Int) => Person = Person.apply _
  
  def genericF[T<:Tuple,I<:Tuple.InverseMap[T,Get],B](f: I => B, t: T): Get[B] = ???
  val person: Get[Person] = genericF(fPerson, t)
case类人物(姓名:String,年龄:Int)
特征得到[A]{
def get:A
}
case类Put[A](get:A)扩展get[A]
val t:(Get[String],Get[Int])=(Put(“Bob”),Put(42))
val fPerson:(字符串,Int)=>Person=Person.apply_
def genericF[T您的代码几乎已经存在了-唯一的问题是
fPerson
的类型是
(String,Int)=>Person
,而不是
((String,Int))=>Person
(使用一个元组而不是两个单独的参数)

下面这个解决方案并不好,尽管它对于TupleXXL可能更有效

extract
是使
PolyFunction
编译,因为它要求对其类型参数应用类型构造函数

AllGs
是为了验证元组只由
Get
s组成,因为正如Dmytro Mitin所指出的,否则它不是类型安全的。如果它都是
Get
s,那么类型就变成了
DummyImplicit
,这是Scala为我们提供的。否则,它就什么都不是了。我想它可能与其他隐式/给定的类型冲突
什么都不在范围之内,但如果你已经有了,你就完蛋了

请注意,只有当您有
Get
时,这才有效。如果您还希望它适用于元组,如
(Put[String],GetSubclass[Int])
,则需要进行一些修改


OP特拉维斯·史蒂文斯(Travis Stevens)通过使用
IsMappedBy
,在不创建
AllGs
的情况下成功地实现了上述解决方案。这就是他们得到的结果():

val fPerson:((字符串,Int))=>Person=Person.apply_
类型ExtractG=[G]=>>G匹配{
案例获取[a]=>a
}
def extract[T g.asInstanceOf[Get[]].Get.asInstanceOf[extract g[g]]
}.A.代替[I]

def通用[T我不认为
Get
是一个HKT-它有一个类型参数,但该参数本身不是一个类型构造函数。首先,
fPerson
不需要两个元组,它需要两个参数,所以我想你需要另外一对括号。我相信
Get
是一个一阶类型,因为你可以通过将类型传递给类型参数,例如
Get[String]
是一种合适的类型。在Scala-3中,Tuple是包含0个或更多元素的Tuple的泛型表示,因此不需要更多的括号。我希望创建genericF能够处理任何大小的Tuple,而不仅仅是示例中所示的2大小的Tuple。@TravisStevens括号在方法参数中不需要:您可以调用
extractt(Put(“Bob”),Put(42))
提取((Put(“Bob”),Put(42))
。但是类型
(String,Int)=>Person
((String,Int))=>Person
是不同的。
提取
不是类型安全的。如果我们调用
提取((1,2))
它将编译,但在运行时抛出
ClassCastException
。@DmytroMitin现在怎么样?可能滥用了
DummyImplicit
,但它似乎很有创造性:)我有点困惑,我们使用高级类型的机器,但仍然必须使用大量
作为
的替代品。我还是把它扔了出去正在解决这个问题,但IsMappedBy会取代AllGs吗?@user是的,好老的
Comapped
+
NatTRel
:)
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)))
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)))