Я наткнулся на эту тему, и там @prolativ предоставил какой-то причудливый синтаксис scala 3 для создания методов расширения для объектов.
extension (int: Int.type)
def unapply(s: String): Option[Int] = s.toIntOption
Но это заставило меня задуматься, есть ли способ сделать что-то подобное в scala 2?
я пробовал
implicit class IntOps(s: Int.type) {
def unapply(s: String): Option[Int] = s.toIntOption
}
Который казался просто переводом scala 2, но он не компилируется с ошибкой object Int is not a case class, nor does it have a valid unapply/unapplySeq member
.
УПД В случае сопоставления с образцом есть способ сделать это - просто добавьте новый объект и сопоставьте его:
object IntOps {
def unapply(s: String): Option[Int] = s.toIntOption
}
но остается вопрос - как добавить методы расширения к объекту?
Неявный класс — это правильный способ добавить метод расширения к объекту.
implicit class IntOps(obj: Int.type) {
def parse(s: String): Option[Int] = s.toIntOption
}
Int.parse("123") // Some(123)
И unapply
можно добавить таким же образом
implicit class IntOps(obj: Int.type) {
def unapply(s: String): Option[Int] = s.toIntOption
}
Int.unapply("123") // Some(123)
Просто это не повлияет на сопоставление с образцом
"123" match {
case Int(i) => ??? // doesn't compile: object Int is not a case class, nor does it have a valid unapply/unapplySeq member
}
Место, где компилятор Scala 2 выбрасывает object Int is not a case class, nor does it have a valid unapply/unapplySeq member
, находится здесь:
https://github.com/scala/scala/blob/v2.13.10/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala#L121-L122
else if (!reallyExists(member))
CaseClassConstructorError(fun, s"${fun.symbol} is not a case class, nor does it have a valid unapply/unapplySeq member")
Это основано на символах (что-то вроде typeOf[Int.type].decl(TermName("unapply"))
в терминах scala-reflect
). Неявные преобразования (методы расширения) здесь не проверяются.
Это изменено в Scala 3: https://github.com/lampepfl/dotty/blob/3.2.2/compiler/src/dotty/tools/dotc/typer/Applications.scala#L1291-L1341 Компилятор пытается для проверки типов x.unapply
, включая неявные преобразования.
Кстати, когда компилятор Scala 2 решает, генерировать ли unapply
в сопутствующем объекте case-класса (Int
не был case-классом), компилятор также не проверяет методы расширения
case class MyClass(i: Int)
MyClass(42) match {
case MyClass(i) => println(i) // 42
}
case class MyClass(i: Int)
object MyClass {
def unapply(mc: MyClass): Option[Int] = Some(100)
}
MyClass(42) match {
case MyClass(i) => println(i) // 100
}
case class MyClass(i: Int)
implicit class MyClassCompanionOps(mc: MyClass.type) {
def unapply(mc: MyClass): Option[Int] = Some(100)
}
MyClass(42) match {
case MyClass(i) => println(i) // 42
}
И это поведение такое же в Scala 3.
Как расширить String, чтобы добавить новую функцию неприменения для использования при извлечении?
Int.type
— это правильный способ добавить методы расширения к объекту-компаньону типаInt
. Просто кажется, что вы не можете добавитьunapply
в качестве расширения в Scala 2.