首页 > 解决方案 > Scala 3 TypeTest over Union 似乎无法正常工作

问题描述

只是玩弄一些 Scala 3 功能,我正在定义一个BooleanAlgebra[A]其中有T <: AB <: A. 这些类型用于检查类型 A 是否已规范化,我们不再需要检查它的边界。

Normailize 给了我们T | F TypeTests 当然是必需的,因为类型TF在运行时被擦除。

该示例可以正常工作,但是它抱怨即使在我匹配的任何时候匹配项都不是详尽无遗的,即使来自和 fromT | F的 TypeTag 也是如此。注意我添加了语句,以便您可以看到它的工作。T | F => TT | F => Fprintln

A => T | F从,添加 TypeTagsA => TA => F不起作用。N 是必需的,因为 Scala 3 似乎不喜欢对可能改变的东西进行这种计算。在 Dotty 网站上的 Peano 示例中,如果您将其切换为 Peano[A],它将停止正确匹配,但如果您添加type NAT = A它就可以正常工作。

任何关于如何使其正确匹配的想法将不胜感激。

这是代码示例:

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)
}

示例实现

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
}

标签: scalaunion-typesscala-3

解决方案


使用以下代码,我没有收到 scala 3.0.2 的任何警告(而且我收到了3.0.0的警告):...

我必须进行一些小的修改,首先是在我定义N为的特征中T | F,并在任何地方使用它:

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

然后,我以稍微不同的方式在对象中定义了扩展(因此将它们从特征中移出)(必须更改名称|&阻止对现有函数的调用Int)。同样在对象中,我定义not了(你有)andor函数(尽管测试不需要它们):

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)

示例用法,有:...

   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

例如,我可以做这样的事情(关于TypeTest代码内使用的评论):...

   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)

推荐阅读