It uses a solution based on a type class. It should be noted that for each (pair) AST node, it is not necessary to manually define instances of a class of a new type. This implies the introduction of a common super-type for each pair (although technically use it as a base class, it is just used as a tag type).
sealed trait Success[T] abstract sealed class Foo abstract sealed class Bar case object FooGood extends Foo with Success[Foo] case object BarGood extends Bar with Success[Bar] sealed trait Failure[T] case object FooBad extends Foo with Failure[Foo] case object BarBad extends Bar with Failure[Bar] @annotation.implicitNotFound("Expecting reciprocal Failure and Success alternatives, but got ${A} and ${B}") trait IsGoodAndBadFacet[A,B] implicit def isGoodAndBadFacet[T,A,B](implicit e1: A <:< Failure[T], e2: B<:<Success[T]): IsGoodAndBadFacet[A,B] = null def go[A, B](x: Int)(implicit e: IsGoodAndBadFacet[A,B]): Either[A, B] = ???
Compliance Check:
scala> go[FooBad.type, BarGood.type](5) <console>:17: error: Expecting reciprocal Failure and Success alternatives, but got FooBad.type and BarGood.type go[FooBad.type, BarGood.type](5) ^ scala> go[FooBad.type, FooGood.type](5) scala.NotImplementedError: an implementation is missing at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225) at .go(<console>:11) ... 33 elided scala> go[BarBad.type, BarGood.type](5) scala.NotImplementedError: an implementation is missing at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225) at .go(<console>:11) ... 33 elided
source share