我想使用Mirror
或其他一些技术来序列化ADT。
我的具体用例是通过通道序列化消息。我可以用Case类对消息进行建模;这很容易。这给了我以下代码:
sealed trait Message
final case class A(a: Int) extends Message
final case class B(b: Int, s: String) extends Message
def serializeMessage(m: Message) =
Tuple.fromProductTyped(m).toList.map(_.serialize) // doesn't work because `m` is a Sum
type Primitive = Int | String
extension (p: Primitive)
def serialize = p match {
case i: Int => s"an Int: $i"
case s: String => s"an String: $s"
}
在我看来,我有两个问题:
- 如何在类型级别保证所有Messsage Case类只包含具有
serialize
方法的字段? - 如何将
m: Message
转换为可对其执行操作的泛型“可序列化”元组?
我可以用match
。那么核心逻辑就是:
def serializeMessage(m: Message) = m match {
case a: A => Tuple.fromProductTyped(a).toList.map(_.serialize)
case b: B => Tuple.fromProductTyped(b).toList.map(_.serialize)
}
这将进行编译。不幸的是,我的API有50多条消息,而且我可能还想支持序列化以外的用例,所以我想自动派生。它完全是机械的,而且非常重复,所以我觉得它“应该”是可行的。
2条答案
按热度按时间sigwle7e1#
您可以使用macro自动执行模式匹配
Scala 3 collection partitioning with subtypes
https://github.com/lampepfl/dotty/discussions/12472
或者,您可以引入一个类型类和derive(例如,使用Shapeless 3)
实际上,在引擎盖下,无形3使用的是
scala.deriving.Mirror
。How to access parameter list of case class in a dotty macro
Using K0.ProductInstances in shapeless3
oyxsuwqo2#
根据Dmytro Mitin的回答(谢谢!),我想出了一个使用Scala
Mirror
的解决方案,它不需要无形状或宏。这在很大程度上基于标准库中的类型类派生示例:https://docs.scala-lang.org/scala3/reference/contextual/derivation.html#mirror
其主旨是:
Serializable[T]
。T
类型的任意元组结构实现类型类示例inline given derived[T]: Serializable[T]
。在这个方法中,我们是using
一个镜像,Scala已经为所有只将case类作为子类型的密封特征提供了镜像(也可以参见上面链接的文档)。Mirror
包含我们需要的所有信息,以获得我们想要序列化的消息的通用元组结构。此消息的类型为T
。match
来确定我们的消息是产品类型还是总和类型。这面镜子保存着必要的信息来弄清楚这一点。我们调用各自的序列化程序。对于elems
参数,我们为所有镜像的元素类型调用(summonAll
)序列化程序类型类示例。例如,如果我们想要序列化case class C(i: Int, s: String)
,那么这就是产品案例,所以我们使用p
,它是产品镜像。p.MirroredElemTypes
是(Int, String)
,Tuple.Map[p.MirroredElemTypes, Serializable]]
是(Serializable[Int], Serializable[String])
。productSerializer
方法(这是两个方法中比较复杂的一个)将这些序列化程序作为elems
参数接收。产品镜像p
告诉我们要序列化的消息类型(m: T
)的结构。elems
是实际的序列化程序。因此,我们将m
和elems
转换为可迭代代码,将它们压缩并将m
的每个部分Map到其序列化版本。然后我们通过mkString
连接序列化的部分。有趣的是,在这一点上,镜子甚至没有被使用。教程也是这样做的,所以我不确定镜子是用来做什么的,但如果我们使用得当,也许我们可以去掉一些instanceOf
。我的实现可能并不理想。given
类型类示例。我以String
和Int
为例。我提供了一个Seq
的示例,它也可以用于任何扩展Seq
的集合。