Reputation: 3390
I'm trying to emulate at least some aspects of union types in scala 2.13.
So far the best approach that meets my needs is coproduct
from shapless. The only problem is that subtyping is not working as I expected. Here is an example:
import shapeless._
type ISB = Int :+: String :+: Boolean :+: CNil
type ISBSub = Int :+: String :+: CNil
implicitly[ISBSub <:< ISB] // error here
I'm trying to create a method with parameter of type ISB
and want this def
accept ISBSub
as well.
def test(t: ISB) = ???
test(Coproduct[ISBSub](2)) // not working
Is there any way to achieve something like this with coproduct
?
Upvotes: 7
Views: 236
Reputation: 139038
Given Shapeless's encoding of coproducts, it's not possible for this kind of subset relationship to be the same as subtyping. Instead there's a type class that provides evidence that one coproduct is a sub-union of another:
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$64@ddd69d2
This type class also allows you to convert values of either coproduct type in either direction:
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))
And then:
scala> shrink[ISB, ISBSub](Coproduct[ISB](1))
res0: Option[ISBSub] = Some(Inl(1))
scala> enlarge[ISB, ISBSub](Coproduct[ISBSub](1))
res1: ISB = Inl(1)
In general it's worth browsing through the contents of the shapeless.ops
packages to get a sense of what kinds of operations are supported for things like coproducts.
Upvotes: 9