scalabook

Форк
0
/
type-test.md 
188 строк · 6.3 Кб

Проверка типа

При сопоставлении с образцом есть две ситуации, когда необходимо выполнить проверку типа во время выполнения. Первый случай — это явная проверка типа с использованием нотации шаблона атрибуции.

(x: X) match
case y: Y =>

Второй случай — когда экстрактор принимает аргумент, не являющийся подтипом контролируемого типа.

(x: X) match
case y @ Y(n) =>
object Y:
def unapply(x: Y): Some[Int] = ...

В обоих случаях проверка класса будет выполняться во время выполнения. Но когда проверка типа относится к абстрактному типу (параметру типа или члену типа), проверка не может быть выполнена, поскольку тип стирается во время выполнения.

Проверку типов в этих случаях можно выполнять если предоставлен trait TypeTest.

package scala.reflect
trait TypeTest[-S, T]:
def unapply(s: S): Option[s.type & T]

Он предоставляет экстрактор, который возвращает свой аргумент, типизированный как T

, если аргумент имеет тип T
. Его можно использовать для кодирования проверки типа.

def f[X, Y](x: X)(using tt: TypeTest[X, Y]): Option[Y] = x match
case tt(x @ Y(1)) => Some(x)
case tt(x) => Some(x)
case _ => None

Чтобы избежать синтаксических издержек, компилятор будет автоматически искать проверку типа, если обнаружит, что проверка типа относится к абстрактным типам. Это означает, что x: Y

трансформируется в tt(x)
и x @ Y(_)
в tt(x @ Y(_))
, если в области видимости доступен контекст TypeTest[X, Y]
. Предыдущий код эквивалентен

def f[X, Y](x: X)(using TypeTest[X, Y]): Option[Y] = x match
case x @ Y(1) => Some(x)
case x: Y => Some(x)
case _ => None

На стороне вызова можно было бы создать тест типа, где тест типа может быть выполнен непосредственно с тестами класса времени выполнения следующим образом.

val tt: TypeTest[Any, String] =
new TypeTest[Any, String]:
def unapply(s: Any): Option[s.type & String] = s match
case s: String => Some(s)
case _ => None
f[AnyRef, String]("acb")(using tt)

Компилятор синтезирует новый экземпляр теста типа, если ни один из них не найден в области видимости, например:

new TypeTest[A, B]:
def unapply(s: A): Option[s.type & B] = s match
case s: B => Some(s)
case _ => None

Если проверки типов не могут быть выполнены, в тесте case s: B =>

будет выдано непроверенное предупреждение.

Наиболее распространенными экземплярами TypeTest

являются те, которые принимают любые параметры (т.е. TypeTest[Any, T]
). Чтобы можно было использовать такие экземпляры непосредственно в границах контекста, предоставляется псевдоним

package scala.reflect
type Typeable[T] = TypeTest[Any, T]

Этот псевдоним можно использовать так:

import scala.reflect.Typeable
def f[T: Typeable]: Boolean =
"abc" match
case x: T => true
case _ => false
f[String]
// res0: Boolean = true
f[Int]
// res1: Boolean = false

Пример

Учитывая следующее абстрактное определение чисел Пеано, которое предоставляет два given экземпляра типов TypeTest[Nat, Zero]

и TypeTest[Nat, Succ]
:

import scala.reflect.*
trait Peano:
type Nat
type Zero <: Nat
type Succ <: Nat
def safeDiv(m: Nat, n: Succ): (Nat, Nat)
val Zero: Zero
val Succ: SuccExtractor
trait SuccExtractor:
def apply(nat: Nat): Succ
def unapply(succ: Succ): Some[Nat]
given typeTestOfZero: TypeTest[Nat, Zero]
given typeTestOfSucc: TypeTest[Nat, Succ]

вместе с реализацией чисел Пеано на основе типа Int

:

object PeanoInt extends Peano:
type Nat = Int
type Zero = Int
type Succ = Int
def safeDiv(m: Nat, n: Succ): (Nat, Nat) = (m / n, m % n)
val Zero: Zero = 0
val Succ: SuccExtractor = new:
def apply(nat: Nat): Succ = nat + 1
def unapply(succ: Succ) = Some(succ - 1)
def typeTestOfZero: TypeTest[Nat, Zero] = new:
def unapply(x: Nat): Option[x.type & Zero] =
if x == 0 then Some(x) else None
def typeTestOfSucc: TypeTest[Nat, Succ] = new:
def unapply(x: Nat): Option[x.type & Succ] =
if x > 0 then Some(x) else None

можно написать следующую программу:

import PeanoInt.*
def divOpt(m: Nat, n: Nat): Option[(Nat, Nat)] =
n match
case Zero => None
case s @ Succ(_) => Some(safeDiv(m, s))
val two = Succ(Succ(Zero))
// two: Int = 2
val five = Succ(Succ(Succ(two)))
// five: Int = 5
println(divOpt(five, two))
// Some((2,1))
println(divOpt(two, five))
// Some((0,2))
println(divOpt(two, Zero))
// None

Обратите внимание, что без TypeTest[Nat, Succ]

паттерн Succ.unapply(nat: Succ)
был бы unchecked
.


Ссылки:

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.