通过在 Scala 3 中展开类型将一个案例 class 转换为另一个案例
Transform a case class to another by unwrapping types in Scala 3
我有一个代表容器和两种情况的枚举 classes:
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 中编写一个通用函数,将所有大小写 classes 如 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)
我尝试了几种方法,但 none 成功了,主要是因为我不明白如何使用 Scala 3 Tuple
在我看来它与 Scala 2 Shapeless' HList
(即使在无形中我也不是那么好)。
我的总体方法是:
- 转换元组中的 class 大小写。为此,我找到了
Tuple.fromProductTyped
- 将每个元素约束为
Container[_]
。我发现 Tuple.IsMappedBy
可以保证元组具有正确的形状,而 Tuple.InverseMap
似乎可以提取容器内的类型。不过,我不确定将这段代码放在哪里。
- 对调用
Container.get
的每个值应用(多边形?)函数。由于我在网上发现的很少,我最终使用了很多 .asInstanceOf
,但对我来说似乎不合适。
- 将生成的
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]
好吧,如果你不介意一点转换,你可以这样做:
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
仅用于证明所有类型转换的类型安全。在我看来,如果没有像 shapeless can 这样的宏,镜像机制就无法以类型安全的方式对事物进行操作。
我有一个代表容器和两种情况的枚举 classes:
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 中编写一个通用函数,将所有大小写 classes 如 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)
我尝试了几种方法,但 none 成功了,主要是因为我不明白如何使用 Scala 3 Tuple
在我看来它与 Scala 2 Shapeless' HList
(即使在无形中我也不是那么好)。
我的总体方法是:
- 转换元组中的 class 大小写。为此,我找到了
Tuple.fromProductTyped
- 将每个元素约束为
Container[_]
。我发现Tuple.IsMappedBy
可以保证元组具有正确的形状,而Tuple.InverseMap
似乎可以提取容器内的类型。不过,我不确定将这段代码放在哪里。 - 对调用
Container.get
的每个值应用(多边形?)函数。由于我在网上发现的很少,我最终使用了很多.asInstanceOf
,但对我来说似乎不合适。 - 将生成的
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]
好吧,如果你不介意一点转换,你可以这样做:
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
仅用于证明所有类型转换的类型安全。在我看来,如果没有像 shapeless can 这样的宏,镜像机制就无法以类型安全的方式对事物进行操作。