“Non può esistenzialmente astratto su tipo parametrico ...”
Domanda
Sono stato nei guai con la Scala 2.8 per divertirsi e cercare di definire un magnaccia che aggiunge un "come" metodo per digitare costruttori, consentendo di convertire da un funtore ad un altro (si prega di trascurare il fatto che io non sono necessariamente trattare con functors qui). Così, per esempio, si potrebbe usare in questo modo:
val array:Array[T]
val list:List[T] = array.as[List]
Quindi, ecco quello che ho cercato di fare:
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
}
}
tuttavia la definizione di naturalTransformations
è contrassegnato con errore "non può esistenzialmente astratto su tipo parametrico G [T]" . Per risolvere questo problema, posso riscrivere naturalTransformations
insieme ad un Transformable
classe aggiuntiva in questo modo:
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)
e sembra funzionare. Ma sembra che il mio primo tentativo dovuto essere equivalente, quindi sono curioso di sapere perchè non è riuscito e quali i mezzi messaggio di errore.
Soluzione
La mia impressione sarebbe che questo è perché a causa delle seguenti dichiarazioni in spec, § 6.11, blocchi:
Un tipo definito localmente tipo definizione t = T è vincolata dalla clausola esistenziale tipo T>: T <: T. È un errore se t trasporta parametri di tipo.
E un'espressione creazione dell'istanza strutturale è valutata ad un blocco, così
new {def greet{println("hello")}}
è un'abbreviazione per
{ class anon$X extends AnyRef{ def greet = println("hello") }; new anon$X }
quindi restituisce un'espressione blocco (secondo § 6.10 della specifica), con la suddetta restrizione. Perché questa restrizione è lì non lo so, però. L'errore che viene generato può essere trovato nella classe TYPERS all'indirizzo questa posizione , che sembra confermare che questa restrizione è la causa dell'errore che si vede. Come lei ha ricordato, che codifica per la funzione in una classe di rimuovere la restrizione dell'espressione di blocco:
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}
Altri suggerimenti
Per me questo suona come una semplicità contro caso generalità: ci potrebbe essere una nuova variabile di tipo generato ogni volta che un blocco è stato creato catturando un certo tipo costruttore di un'istanza di tipo esistenziale, ma che renderebbe la diagnostica degli errori più difficile da capire.
Si noti inoltre che avere una classe trasforma la chiamata in un'INVOKEVIRTUAL veloce, anziché invocare come () metodo per riflessione.