【问题标题】:Transform a case class to another by unwrapping types in Scala 3通过在 Scala 3 中展开类型将案例类转换为另一个案例类
【发布时间】:2021-06-07 17:07:47
【问题描述】:

我有一个表示一个容器和两个案例类的枚举:

enum Container[+A]:
  case Value(value: A)
  case Default(default: A)

  def get: A = this match
    case Value(value)     => value
    case Default(default) => default

case class PersonTemplate(name: Container[String], age: Container[Int], enabled: Container[Boolean])
case class Person(name: String, age: Int, enabled: Boolean)

我想在 Scala 3 中编写一个通用函数,将所有案例类(如 PersonTemplate)转换为对应的类(如 Person),类似于:

def extract[I <: Product, O <: Product](input: I): O = ???

val initial = PersonTemplate(Value("John"), Default(12), Default(true))
val final = extract[PersonTemplate, Person](initial)
// Result: Person("John", 12, true)

我尝试了几种方法,但没有一个是成功的,主要是因为我不明白如何使用 Scala 3 Tuple 这在我看来与 Scala 2 Shapeless' HList 不同(即使在 shapeless 我也是不太好)。

我的总体方法是:

  1. 在元组中转换案例类。为此我找到了Tuple.fromProductTyped
  2. 将每个元素限制为Container[_]。我发现Tuple.IsMappedBy 保证元组具有正确的形状和Tuple.InverseMap 似乎提取容器内的类型。不过,我不确定将这段代码放在哪里。
  3. 对调用Container.get 的每个值应用(poly?)函数。由于我在网上找到的一点点,我最终使用了很多 .asInstanceOf,但我觉得它并不合适。
  4. 使用summon[Mirror.Of[O]].fromProduct(output) 将生成的Tuple 转换为输出类型

为了完整起见,这是我尝试的最后一个代码,当然不起作用:

def resolve[I <: Product: Mirror.ProductOf, O: Mirror.ProductOf](input: I): O =
  val processed =
    Tuple
      .fromProductTyped(input)
      .map { [T] => (value: T) => ??? }

  summon[Mirror.Of[O]].fromProduct(processed)

type ExtractG = [G] =>> G match {
  case Container[a] => a
}

def process[I <: Tuple, O <: Tuple](input: I)(using Tuple.IsMappedBy[Container][I]): O =
  input.map { [A] => (a: A) =>
    a.asInstanceOf[Container[_]].get.asInstanceOf[ExtractG[A]]
  }.asInstanceOf[O]

【问题讨论】:

    标签: scala generics scala-3 generic-derivation match-types


    【解决方案1】:

    好吧,如果你不介意演员,你可以这样做:

    def unwrapper[From <: Product, To](
      using To: Mirror.ProductOf[To],
      From: Mirror.ProductOf[From],
      ev: From.MirroredElemTypes =:= Tuple.Map[To.MirroredElemTypes, Container]
    ): (From => To) =
      from => To.fromProduct {
        from.productIterator
            .toArray
            .map(_.asInstanceOf[Container[_]].get)
            .foldRight[Tuple](EmptyTuple)(_ *: _)
      }
    
    @main def run =
      import Container._
      val unTemplate = unwrapper[PersonTemplate, Person]
      println(unTemplate(PersonTemplate(Value("foo"), Default(42), Default(false))))
    

    请求的From 和ev 仅用于证明所有转换的类型安全。 IMO 镜像机器缺乏在没有像 shapeless can 这样的宏的情况下以类型安全的方式对事物进行操作的能力。

    【讨论】:

    • 是的,我明白你的意思了。你知道 Scala 3 是否正在更新 shapeless 吗?
    • v3.0.0-M1 分支中有一些工作正在进行,但当前工作代码的最大部分与类型类派生有关。我不确定 shapeless 是否仍将拥有与 Scala 2 中相同的类型安全元编程库
    猜你喜欢
    • 1970-01-01
    • 2016-07-27
    • 2014-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-08
    • 1970-01-01
    • 2020-03-07
    相关资源
    最近更新 更多