4

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

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(即使在无形中我也不是那么好)。

我的总体方法是:

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

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

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]
4

1 回答 1

2

好吧,如果您不介意一些演员表,您可以这样做:

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))))

要求Fromev仅用于证明所有铸件的类型安全。IMO 镜像机器缺乏以类型安全的方式对事物进行操作的能力,而没有像无形罐这样的宏。

于 2021-03-11T13:45:22.390 回答