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

Экзистенциальные типы и члены типа

Что работает (часть A)

Предположим, что у меня есть черта с параметром типа:

trait A[T]

Я могу использовать экзистенциальный тип для записи метода, который возьмет коллекцию A, у которой все те же T:

def foo(as: Seq[A[X]] forSome { type X }) = true

Обратите внимание, что это отличается от следующего:

def otherFoo(as: Seq[A[X] forSome { type X }]) = true

Или эквивалент:

def otherFoo(as: Seq[A[_]]) = true

В этих случаях объем экзистенциальности находится внутри Seq, поэтому A может иметь разные T s. С моим оригинальным foo (с экзистенциальным охватом над Seq), все в порядке:

foo(Seq(new A[Int] {}, new A[Int] {}))

Но измените параметры типа и не скомпилируйте:

scala> foo(Seq(new A[Int] {}, new A[String] {}))
<console>:10: error: type mismatch;
 found   : Seq[A[_ >: java.lang.String with Int]]
 required: Seq[A[X]] forSome { type X }

              foo(Seq(new A[Int] {}, new A[String] {}))
                     ^

Это все довольно просто.

Что работает (часть B)

Теперь предположим, что у меня есть аналогичная черта с членом типа вместо параметра типа:

trait B { type T }

Я могу написать метод, который будет принимать только B с некоторым заданным T:

scala> def bar[X](b: B { type T = X }) = true
bar: [X](b: B{type T = X})Boolean

scala> bar[Int](new B { type T = Int })
res5: Boolean = true

scala> bar[String](new B { type T = Int })
<console>:10: error: type mismatch;
 found   : java.lang.Object with B
 required: B{type T = String}
              bar[String](new B { type T = Int })
                          ^

Опять же, это работает точно так, как вы ожидали.

Что не работает

Когда мы пытаемся написать эквивалент нашего foo выше, но для членов типа вещи становятся странными.

scala> def baz(bs: Seq[B { type T = X }] forSome { type X }) = true
baz: (as: Seq[B{type T = X}] forSome { type X })Boolean

scala> baz(Seq(new B { type T = Int }, new B { type T = String }))
res7: Boolean = true

То, что последняя компиляция строк не имеет для меня никакого смысла. Я сказал, что хочу, чтобы все члены типа были одинаковыми. Мой foo показывает, что я могу сделать это для параметров типа, а bar показывает, что я могу ограничить тип, основанный на его членах типа. Но я не могу объединить эти два.

Я пробовал это на 2.9.2 и 2.10.0-M5.

Мотивация

Этот вопрос вдохновлен этим, где моя первая мысль была, о, просто используйте экзистенциальный тип (отбросив на секунду проблему, которая представляется невозможным получить экзистенциальный тип для области типа повторяющегося параметра, что было бы удобно здесь):

def accept(rs: Seq[RList[Int] { type S = X }] forSome { type X }) = true

Но это на самом деле не работает - вы получаете тот же странный результат, что и в упрощенном примере выше.

4b9b3361

Ответ 1

Я, наконец, разобрался (по крайней мере, надеюсь). Пусть делают это по-другому. Мы строим нашу черту:

scala> trait B {type T}
defined trait B

Мы попытаемся построить последовательность B:

scala> Seq(new B {type T = Int}, new B {type T = String})
res0: Seq[B{type T >: String with Int}] = List([email protected], [email protected])

Черт, это работает! Хорошо, у нас нет равенства для type T, но давайте играть с ним:

scala> res0 : (Seq[B {type T = X}] forSome {type X >: String with Int})
res1: Seq[B{type T = X}] forSome { type X >: String with Int } = List([email protected], [email protected])

Это ближе. Нет, подождите, он не ближе, лучше, чем вы предлагали в качестве параметра baz, мы не только предоставляем сырой тип, но и верхнюю границу! Таким образом, мы можем четко передать его на baz. Вот почему он не работает, как вы ожидали.

Ответ 2

В вашем примере:

scala> def baz(bs: Seq[B { type T = X }] forSome { type X }) = true

сообщает компилятору, что функция baz берет Seq из свойства B. В качестве признака B используется элемент типа T, но, насколько это касается компилятора, он полностью прекрасен для функции baz, чтобы взять Seq признака B, даже если типы члена T типа B не совпадают. Если вы хотите, чтобы функция baz взяла Seq из признака B с теми же членами типа, вам нужно сообщить компилятору следующее:

scala> def baz[X](bs: Seq[B { type T = X }]) = true
baz: [X](bs: Seq[B{type T = X}])Boolean


scala> baz[Int](Seq(new B { type T = Int }, new B { type T = String }))
    <console>:10: error: type mismatch;
    found   : java.lang.Object with B
    required: B{type T = Int}
              baz[Int](Seq(new B { type T = Int }, new B { type T = String }))
                                               ^

scala> baz[Int](Seq(new B { type T = Int }, new B { type T = Int }))
res10: Boolean = true