scalabook

Форк
0
/
match-types.md 
125 строк · 4.9 Кб

Типы match

Тип match сводится к одной из его правых частей, в зависимости от проверяемого типа. Например:

type Elem[X] = X match
case String => Char
case Array[t] => t
case Iterable[t] => t

Матчинг определяет тип, который сокращается следующим образом:

Elem[String]       =:=  Char
Elem[Array[Int]]   =:=  Int
Elem[List[Float]]  =:=  Float
Elem[Nil.type]     =:=  Nothing

Здесь под =:=

подразумевается, что левая и правая стороны являются взаимными подтипами друг друга.

В общем случае тип match имеет вид:

S match { P1 => T1 ... Pn => Tn }

где S, T1, ..., Tn

- типы, а P1, ..., Pn
- шаблоны типов. Переменные типа в шаблонах, как обычно, начинаются со строчной буквы.

Типы match могут составлять часть определений рекурсивных типов. Пример:

type LeafElem[X] = X match
case String => Char
case Array[t] => LeafElem[t]
case Iterable[t] => LeafElem[t]
case AnyVal => X

Определения рекурсивного типа match также могут иметь верхнюю границу, например:

type Concat[Xs <: Tuple, +Ys <: Tuple] <: Tuple = Xs match
case EmptyTuple => Ys
case x *: xs => x *: Concat[xs, Ys]

В этом определении известно, что каждый экземпляр Concat[A, B]

, редуцированный или нет, является подтипом Tuple
. Это необходимо для проверки типа рекурсивного вызова x *: Concat[xs, Ys]
, поскольку *:
требует в качестве правого операнда Tuple
.

Зависимая типизация

Типы match можно использовать для определения методов с зависимыми типами. Например:

def leafElem[X](x: X): LeafElem[X] = x match
case x: String => x.charAt(0)
case x: Array[t] => leafElem(x(0))
case x: Iterable[t] => leafElem(x.head)
case x: AnyVal => x

Результат:

leafElem(Array(List("abc")))
// res0: Char = 'a'

Этот специальный режим ввода выражений match используется только при выполнении следующих условий:

  • шаблоны выражений match не имеют условий (guards)
  • тип проверяемого выражения match является подтипом типа match
  • выражение match и тип match имеют одинаковое количество cases
  • все шаблоны выражений match являются типизированными шаблонами, и эти типы относятся к соответствующим шаблонам типов как =:=
    .

Termination

Определения типов match могут быть рекурсивными, а это означает, что при сокращении типов match можно попасть в бесконечный цикл.

Поскольку редукция связана с созданием подтипов, есть механизм обнаружения циклов. В результате следующее выдаст разумное сообщение об ошибке:

type L[X] = X match
case Int => L[X]
def g[X]: L[X] = ???
|  val x: Int = g[Int]
|                  ^
|  Recursion limit exceeded.
|  Maybe there is an illegal cyclic reference?
|  If that's not the case, you could also try to
|  increase the stacksize using the -Xss JVM option.
|  A recurring operation is (inner to outer):
|  
|    subtype LazyRef(Test.L[Int]) <:< Int

Внутри компилятор Scala обнаруживает такие циклы, превращая выбранные переполнения стека в ошибки типов. Если во время создания подтипа происходит переполнение стека, исключение будет перехвачено и преобразовано в ошибку времени компиляции, указывающую на трассировку тестов подтипа, вызвавшую переполнение, без отображения полной трассировки стека.

Match Types Variance

Все позиции типа в типе match (проверяемые, шаблоны, тела) считаются инвариантными.


Ссылки:

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

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

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

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