Подтвердить что ты не робот

Используйте пример и примеры для шаблона типа с переменной типа

Я узнал, что спецификация spec scala поддерживает переменные типа привязки при выполнении соответствия шаблону типа:

Map(1 -> "one", 2 -> "two") match {
  case l: Map[k, v] =>
    // binds k to Int and v to String
    // k and v are types as shown here:
    val i: Iterator[Tuple2[k, v]] = l.iterator
    println(i.mkString(", "))
}

Есть ли какие-нибудь причудливые вещи или практические вещи, которые я могу сделать с этим? Или переменные типа привязки полезны только для целей документирования типов?

Мне пришло в голову, что scala иногда нужны аннотации типа, такие как определение функции, поэтому я попытался:

def prepender(obj: Any) = obj match {
  case xs: List[a] => (x: a) => x :: xs
  case opt: Some[a] => (x: a) => x :: Nil
}

Но тогда тип возвращаемой функции является странным:

prepender: (obj: Any)a with a => List[Any] forSome { type a; type a }

scala> val p = prepender(List(1,2))
p: a with a => List[Any] forSome { type a; type a } = <function1>

scala> p(1)
<console>:10: error: type mismatch;
 found   : Int(1)
 required: a(in value res7) with (some other)a(in value res7) where 
   type (some other)a(in value res7), type a(in value res7)
4b9b3361

Ответ 1

Я надеюсь, что это не слишком долго, но я серьезно сомневаюсь в этом, поэтому я сначала попытаюсь дать быстрый ответ: "Когда вы называете (абстрактным) что-то, основное использование имеет в виду это позже". Ну, это было не полезно сейчас, не так ли?

Рассмотрим эту простую функцию Scala:

val sum = (a: Int, b: Int) => a + b

Компилятору не нужно знать, что a является a и b является b. Все, что нужно знать, что a, а также b имеют тип Int и что a предшествует b (что в данном случае не имеет значения, так как добавление является коммутативным, но компилятор все равно заботится!). Scala предлагает (не поймите меня неправильно, я также его люблю) компилятор, дружественный синтаксис заполнителя, который выступает в качестве доказательства этой "гипотезы".

val sum: (Int, Int) => Int = _ + _ // where the 1st _ differs from the 2nd _

Теперь взгляните на это:

case x: SomeTypeParameterizedWith[AnotherType] // AnotherType is erased anyway
case x: SomeParameterizedType[_] // Existential type
case x: SomeParameterizedType[kind] // Existential type which you can reference

Если вам не нужен аргумент типа, используйте синтаксис placeholder. Когда вы делаете (по какой-либо причине) заботу, вы должны называть аргумент типа нижним регистром, поэтому компилятор знает, что вы хотите рассматривать его как идентификатор.

Вернуться к вашему вопросу.

Основное использование экзистенциальных типов работает с шаблонами подстановок Java. Это взято из Программирование в Scala - Экзистенциальные типы и было слегка изменено вашим.

// This is a Java class with wildcards
public class Wild {
  public java.util.Collection<?> contents() {
    java.util.Collection<String> stuff = new Vector<String>();
    stuff.add("a");
    stuff.add("b");
    stuff.add("see");
    return stuff;
  }
}

// This is the problem
import scala.collection.mutable.Set
val iter = (new Wild).contents.iterator
val set = Set.empty[???] // what type goes here?
while (iter.hasMore)
  set += iter.next()

// This is the solution
def javaSet2ScalaSet[T](jset: java.util.Collection[T]): Set[T] = {
  val sset = Set.empty[T] // now T can be named!
  val iter = jset.iterator
  while (iter.hasNext)
    sset += iter.next()
  sset
}

Хорошо, так что же произошло? Простые дженерики, нет волшебства?! Если вы имеете дело с дженериками на повседневной основе, это выглядит нормально для вас, но вы забываете, что ультра-супер-концепция ввода аргументов типа в область видимости работает только по классам и методам. Что делать, если вы находитесь вне класса или метода, просто в какой-то случайной области в середине нигде (например, REPL)? Или что, если вы находитесь в классе или методе, но аргументы типа не были введены в их области? Вот где ваш вопрос и этот ответ приходят в игру.

val set = new Wild().contents match {
  case jset: java.util.Collection[kind] => {
    val sset = Set.empty[kind]
    val iter = jset.iterator
    while (iter.hasNext)
      sset += iter.next()
    sset
  }
}

Идентификатор kind требуется, чтобы компилятор мог проверить, что вы ссылаетесь на одно и то же.

Обратите внимание, что вы не можете просто добавлять строки в set, так как тип set равен Set[_].