简体   繁体   中英

Scala 3 TypeTest over Union doesn't seem to work correctly

Just playing around with some Scala 3 features, I'm defining a BooleanAlgebra[A] in which there is T <: A and B <: A . These types are used to check if the type A has been normalized and we no longer need to check the bounds of it.

Normailize gives us T | F T | F TypeTests are required of course because the types T and F are erased at runtime.

The sample works without a problem, however it complains that the matches are not exhaustive anytime I match against T | F T | F even though there is a TypeTag from T | F => T T | F => T and from T | F => F T | F => F . Note I've added println statements so that you can see it work.

Adding TypeTags from A => T | F A => T | F , A => T and A => F don't work either. N is required due to the fact that Scala 3 doesn't seem to like to do this calculation over something that can change. In the Peano example on the Dotty site, if you switch it to Peano[A] it will stop matching properly, but if you add type NAT = A it works fine.

Any thoughts about how this can be made to match properly would be appreciated.

Here is the code sample:

trait BooleanAlgebra[A] {
  final type N = A
  type T <: N
  type F <: N

  val tru: T
  val fls: F

  final given TypeTest[T | F, T] =
    x =>
      println(" => T")
      if x == tru then Some(tru.asInstanceOf[x.type & T])
      else None

  final given TypeTest[T | F, F] =
    x =>
      println(" => F")
      if x == fls then Some(fls.asInstanceOf[x.type & F])
      else None

  def normalize(value: N): T | F

  final def not(value: T | F): T | F =
    value match
      case _: T => fls
      case _: F => tru

  final def and(lhs: => T | F, rhs: => T | F): T | F =
    lhs match
      case _: T => rhs
      case _: F => fls

  final def or(lhs: => T | F, rhs: => T | F): T | F =
    lhs match
      case _: T => tru
      case _: F => rhs

  extension (lhs: => T | F) {
    final def unary_! : T | F =
      not(lhs)

    final def |(rhs: => T | F): T | F =
      or(lhs, rhs)

    final def &(rhs: => T | F): T | F =
      and(lhs, rhs)
  }
}

object BooleanAlgebra {
  def tru[A](using b: BooleanAlgebra[A]): b.T =
    b.tru

  def fls[A](using b: BooleanAlgebra[A]): b.F =
    b.fls

  def not[A](using b: BooleanAlgebra[A]): b.T | b.F => b.T | b.F =
    value =>
      b.not(value)

  def norm[A](value: A)(using b: BooleanAlgebra[A]): b.T | b.F =
    b.normalize(value)
}

Example implementation

given BooleanAlgebra[Int] with {
  type T = 1
  type F = 0

  val tru = 1
  val fls = 0

  def normalize(value: Int) =
    if value > 0 then tru
    else fls
}

With the following code, I'm NOT getting any warnings with scala 3.0.2 (and I WAS getting them with 3.0.0):...

I had to make several minor modifications, first in the trait I've defined N as T | F T | F , and used it everywhere:

trait BooleanAlgebra [A]:
   type T <: A
   type F <: A
   type N = T | F

   val tru: T
   val fls: F

   final given TypeTest [N, T] =
      x =>
         print ("=> T ")
         if x == tru then
            Some (tru.asInstanceOf [x.type & T])
         else
            None

   final given TypeTest [N, F] =
      x =>
         print ("=> F ")
         if x == fls then
            Some (fls.asInstanceOf [x.type & F])
         else
            None

   def normalize (value: A): N

   final def not (v1: => N): N =
      v1 match
         case _: T => fls
         case _: F => tru

   final def and (v1: => N, v2: => N): N =
      v1 match
         case _: T => v2
         case _: F => fls

   final def or (v1: => N, v2: => N): N =
      v1 match
         case _: T => tru
         case _: F => v2

Then, I've defined extension(s) in the object (so moved them from the trait) in a slightly different manner (had to change names for | and & to prevent calls to existing functions in Int too). Also in the object, I've defined not (which you had), and and or functions (although they are not needed for the test):

object BooleanAlgebra:
   def tru [A] (using b: BooleanAlgebra [A]): b.T =
      b.tru

   def fls [A] (using b: BooleanAlgebra [A]): b.F =
      b.fls

   def norm [A] (value: A) (using b: BooleanAlgebra [A]): b.N =
      b.normalize (value)

   extension [A] (using b: BooleanAlgebra [A]) (v1: => b.N)
   {
      final def unary_! : b.N =
         b.not (v1)

      final def &&& (v2: => b.N): b.N =
         b.and (v1, v2)

      final def ||| (v2: => b.N): b.N =
         b.or (v1, v2)
   }

   def not [A] (using b: BooleanAlgebra [A]): b.N => b.N =
      v1 => b.not (v1)

   def and [A] (using b: BooleanAlgebra [A]): (b.N, b.N) => b.N =
      (v1, v2) => b.and (v1, v2)

   def or [A] (using b: BooleanAlgebra [A]): (b.N, b.N) => b.N =
      (v1, v2) => b.or (v1, v2)

Example usage, with:...

   given BooleanAlgebra [Int] with
      type T = 1
      type F = 0

      val tru = 1
      val fls = 0

      def normalize (value: Int): N =
         if (value > 0) then 1 else 0

I can do something like this for example (comments regarding use of TypeTest within code):...

   def test () (using ba: BooleanAlgebra [Int]): Unit =
      import ba._
      val a1 = 4
      val n1 = normalize (a1)
      // this will print normalized value of 1
      println (n1)
      // following not call will make a single type test for T
      val n2 = !n1
      println (n2)
      // following two calls will make 1 type test each, for T only
      println (n1 &&& n2)
      println (n1 ||| n2)
      // following two calls will make 2 type tests each, both for T (failing) and then F
      println (n2 &&& n1)
      println (n2 ||| n1)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM