首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Scala 3-一阶型包装器和InverseMap元组的提取

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

Stack Overflow用户
提问于 2020-10-13 16:43:21
回答 1查看 793关注 0票数 4

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

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

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

因此,最终目标是编写一个函数,它可以使用任意数量的Get[_]类型的元组,以及一个输入与InserveMap类型匹配的函数,最后返回一个Get[_],其中包装的类型是函数的结果。

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

代码语言:javascript
复制
  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)

我在这里设置了一个https://scastie.scala-lang.org/OleTraveler/QIyNHPLHQIKPv0lgsYbujA/23

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-10-13 18:06:03

您的代码几乎已经是编撰了--唯一的问题是,fPerson(String, Int) => Person类型的,而不是((String, Int)) => Person类型的(使用一个元组而不是两个单独的参数)。

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

代码语言:javascript
复制
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)))

这里是使用Tuple.InverseMap实现genericF的一种方法(请注意,我将这两个参数转换为genericF )。

代码语言:javascript
复制
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将验证元组仅由Gets组成,因为正如Dmytro所指出的,否则它不是类型错误。如果是所有的Get,类型就变成了DummyImplicit,Scala为我们提供了这个类型。否则,就是Nothing。我想它可能与其他隐式/给定范围内的Nothing相冲突,但是如果您已经有了一个,那么无论如何,您都会陷入困境。

请注意,这只在您有Get时才能起作用,如果您也希望它适用于像(Put[String], GetSubclass[Int])这样的元组,则需要进行一些修改。

操作人员Travis通过使用AllGs,成功地使上面的解决方案在不创建IsMappedBy的情况下工作。这就是他们得到的(斯卡斯蒂):

代码语言:javascript
复制
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)))

这里有一个使用依赖类型,只是为了好玩(斯卡斯蒂):

代码语言:javascript
复制
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仍然是必要的。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64339583

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档