1

我正在探索 scala3 内联的可能性。我制作了一个简单的示例,我希望在编译时应用构造密封类的验证方法:

import scala.compiletime.{ error, codeOf }

sealed abstract class OneOrTwo(val code: String)

object OneOrTwo {

    case object One extends OneOrTwo("one")
    case object Two extends OneOrTwo("two")

    def from(s: String): Option[OneOrTwo] = 
        s match {
            case One.code => Some(One)
            case Two.code => Some(Two)
            case _ => None
        }

    inline def inlinedFrom1(s: String): OneOrTwo = {
        inline s match {
            case "one" => One
            case "two" => Two
            case _ => error("can't make a OneOrTwo out of " + codeOf(s))
        } 
    }

    val test1 = OneOrTwo.inlinedFrom1("one") // compiles 
    val test12 = OneOrTwo.inlinedFrom1("three") // doesn't compile as expected -> can't make a OneOrTwo out of "three"
}

到现在为止还挺好。但我真正想要的是能够重用from内联函数中的函数。这是我尝试:

    // this goes in the OneOrTwo companion object as well

    inline def inlinedFrom2(s: String): OneOrTwo = {
        from(s).getOrElse(error("can't make a OneOrTwo out of " + codeOf(s)))
    }

    inline def inlinedFrom3(s: String): OneOrTwo = {
        s match {
            case One.code => One
            case Two.code => Two
            case _ => error("can't make a OneOrTwo out of " + codeOf(s))
        }
    }

    
    val test2 = OneOrTwo.inlinedFrom2("one") // doesn't compile -> can't make a OneOrTwo out of "one"
    val test3 = OneOrTwo.inlinedFrom3("one") // doesn't compile -> can't make a OneOrTwo out of "one"

inlinedFrom3是一种概括,表明如果我直接匹配对象的代码,编译器不会看到它们与输入字符串相同并采用错误的分支。

我想了解的是为什么inlinedFrom3不起作用以及是否有办法使它起作用。

注意:我使用的是 scala 3.0.0-RC2

4

1 回答 1

4

好问题,

首先,考虑内联是在 Scala 中在编译时执行元编程的一种方式(正如您在示例中所做的那样)。

为了赌上你的问题,这里编译器不明白那One.code是 type "one"。实际上,如果您尝试 print One.code,编译器输出是:

OneOrTwo.One.code

并不是

"one"

但是等等,为什么?因为编译器在编译时无法推断出有关 One.code 的任何信息。要将缺失的信息提供给编译器,您可以:

  • 指定单例字符串类型
sealed trait OneOrTwo {
  val code: String
}

object OneOrTwo {
    case object One extends OneOrTwo {
      override val code : "one" = "one" 
    }
    case object Two extends OneOrTwo {
      override val code : "two" = "two"
    }
}
  • 内联val定义
sealed trait OneOrTwo {
  val code: String
}

object OneOrTwo {
    case object One extends OneOrTwo {
      override inline val code = "one" 
    }
    case object Two extends OneOrTwo {
      override inline val code = "two"
    }
}

即使进行了这种修改,您的代码在编译中仍然会失败。它是由未内联的 match 子句引起的。实际上,正如这里提到的, error 方法提供了一种发出自定义错误消息的方法。如果对错误的调用是内联的并且没有作为死分支消除,则会发出错误。 所以如果你error(...)输入一个没有内联的代码,总是会抛出一个错误。例如,这段代码:

if(false) { error("thrown..") } { }

产生编译错误。

最后,要解决您的问题,您需要内联匹配子句:

inline def inlinedFrom3(s: String): OneOrTwo = {
   inline s match {
       case One.code => One
       case Two.code => Two
       case _ => error("can't make a OneOrTwo out of " + codeOf(s))
   }
}

编译 (for inlinedFrom3) 成功。inlineFrom2由于上述问题而继续失败。

于 2021-04-10T12:55:33.010 回答