Встраивание scala 3 не выполняется - очень простой пример
Я изучаю возможности встраивания в 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
не работает и есть ли способ заставить его работать.
Примечание: я использую scala 3.0.0-RC2
1 ответ
Хороший вопрос,
Прежде всего, учтите, что встраивание - это способ выполнять метапрограммирование во время компиляции в Scala (как вы это делали в своем примере).
Чтобы сделать ставку на вашу проблему, здесь компилятор не понимает, что это тип
"one"
. Действительно, если вы попытаетесь напечатать
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 встроен и не устранен как мертвая ветвь. Итак, если вы положите
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))
}
}
Сборник (для
inlinedFrom3
) удалось.
inlineFrom2
продолжает выходить из строя из-за вышеупомянутой проблемы.
Надеюсь, я вам помог.