"не может быть экстенсивно абстрактным по параметризованному типу..."
Я был возился с Scala 2.8 для удовольствия и пытался определить сутенер, который добавляет метод "как" для ввода конструкторов, позволяя конвертировать из одного функтора в другой (пожалуйста, обратите внимание на то, что я не обязательно имею дело с функторами здесь). Например, вы можете использовать его следующим образом:
val array:Array[T]
val list:List[T] = array.as[List]
Итак, вот что я пытался сделать:
object Test {
abstract class NatTrans[F[_], G[_]] {
def convert[T](f:F[T]):G[T]
}
implicit def array2List:NatTrans[Array, List] = new NatTrans[Array, List] {
def convert[T](a:Array[T]) = a.toList
}
// this next part gets flagged with an error
implicit def naturalTransformations[T, F[_]](f:F[T]) = new {
def as[G[_]](implicit n:NatTrans[F, G]) = n convert f
}
}
однако определение naturalTransformations
с ошибкой "не может быть экстенсивно абстрактным по параметризованному типу G [T]" . Чтобы исправить это, я могу переписать naturalTransformations
вместе с дополнительным классом Transformable
следующим образом:
class Transformable[T, F[_]](f:F[T]) {
def as[G[_]](implicit n:NatTrans[F, G]) = n convert f
}
implicit def naturalTransformations[T, F[_]](f:F[T]) = new Transformable[T, F](f)
и, похоже, он работает. Но похоже, что моя первая попытка должна была быть эквивалентной, поэтому мне любопытно, почему это не удалось и что означает сообщение об ошибке.
Ответы
Ответ 1
Моя догадка заключается в том, что это происходит из-за следующих утверждений в спецификации, & sect; 6.11, блоки:
Локально определенный тип определения типа t = T связан экзистенциальным предложением тип t > : T <: T. Это ошибка, если t несет параметры типа.
И выражение выражения структурного экземпляра оценивается блоком, поэтому
new {def greet{println("hello")}}
является сокращением для
{ class anon$X extends AnyRef{ def greet = println("hello") }; new anon$X }
поэтому он вычисляет блочное выражение (согласно § 6.10 спецификации) с вышеупомянутым ограничением. Почему это ограничение есть, я не знаю, однако. Выброшенная ошибка может быть найдена в классе "Типы" в это местоположение, которое, похоже, подтверждает, что это ограничение является причиной ошибки, которую вы видеть.
Как вы упомянули, кодирование функции в классе устраняет ограничение блочного выражения:
scala> class N[M[_]]
defined class N
scala> class Q { def as[M[_]](n:N[M]) = null}
defined class Q
scala> new { def as[M[_]](n:N[M]) = null}
:7: error: can't existentially abstract over parameterized type M
new { def as[M[_]](n:N[M]) = null}
Ответ 2
Для меня это выглядит как простота в отношении общности: может возникать новая переменная типа, генерируемая каждый раз, когда создается блок, захватывая некоторый конструктор типов, созданный с помощью экзистенциального типа, но это затрудняет понимание ошибок.
Также обратите внимание, что если класс превращает вызов в быстрый INVOKEVIRTUAL, а не вызывает метод as() путем отражения.