无形副产品子类型

Shapless coproduct sub-typing

我试图在 scala 2.13 中至少模拟联合类型的某些方面。

到目前为止,满足我需求的最佳方法是来自 shapless 的 coproduct。唯一的问题是子类型没有像我预期的那样工作。这是一个例子:

import shapeless._

type ISB = Int :+: String :+: Boolean :+: CNil

type ISBSub = Int :+: String :+: CNil

implicitly[ISBSub <:< ISB] // error here

我正在尝试创建一个参数类型为 ISB 的方法,并希望此 def 也接受 ISBSub

def test(t: ISB) = ???

test(Coproduct[ISBSub](2)) // not working

有什么方法可以用 coproduct 实现这样的目标吗?

鉴于 Shapeless 对联积的编码,这种子集关系不可能与子类型相同。相反,有一种类型 class 提供了一个副产品是另一个副产品的子联合的证据:

scala> import shapeless._
import shapeless._

scala> type ISB = Int :+: String :+: Boolean :+: CNil
defined type alias ISB

scala> type ISBSub = Int :+: String :+: CNil
defined type alias ISBSub

scala> shapeless.ops.coproduct.Basis[ISB, ISBSub]
res0: shapeless.ops.coproduct.Basis[Int :+: String :+: Boolean :+: shapeless.CNil,Int :+: String :+: shapeless.CNil]{type Rest = Boolean :+: shapeless.CNil} = shapeless.ops.coproduct$Basis$$anon@ddd69d2

此类型 class 还允许您沿任一方向转换任一余积类型的值:

import shapeless.ops.coproduct.Basis

def shrink[A <: Coproduct, B <: Coproduct](a: A)(implicit ev: Basis[A, B]): Option[B] =
  ev(a).toOption

def enlarge[A <: Coproduct, B <: Coproduct](b: B)(implicit ev: Basis[A, B]): A =
  ev.inverse(Right(b))

然后:

scala> shrink[ISB, ISBSub](Coproduct[ISB](1))
res0: Option[ISBSub] = Some(Inl(1))

scala> enlarge[ISB, ISBSub](Coproduct[ISBSub](1))
res1: ISB = Inl(1)

一般来说,值得浏览 shapeless.ops 包的内容,以了解像联积这样的东西支持哪些类型的操作。