2010-06-26 6 views
11

Je lisais avec Scala 2.8 pour m'amuser et essayais de définir un pimp qui ajoute une méthode "as" pour taper des constructeurs, permettant de convertir d'un foncteur à un autre (veuillez ignorer le fait que je n'ai pas nécessairement affaire à des foncteurs ici). Ainsi, par exemple, vous pouvez l'utiliser comme ceci:"ne peut pas existentiellement abstrait sur type paramétré ..."

val array:Array[T] 
val list:List[T] = array.as[List] 

Alors, voici ce que j'ai essayé de le faire:

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 
    } 
} 

cependant la définition de naturalTransformations est marqué avec l'erreur « ne peut pas existentiellement abstraite sur le type paramétré G [T] ". Pour résoudre ce problème, je peux réécrire naturalTransformations avec une classe supplémentaire Transformable comme ceci:

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) 

et il semble fonctionner. Mais il semble que ma première tentative aurait dû être équivalente, alors je suis curieux de savoir pourquoi cela a échoué et ce que signifie le message d'erreur.

+1

J'ai l'habitude de voir l'erreur "Le type de paramètre dans l'affinement structurel peut ne pas faire référence à un type abstrait défini en dehors de ce raffinement" dans des situations similaires. Cette restriction est liée à la façon dont les types structurels sont implémentés sur la JVM avec réflexion, IIRC. http://stackoverflow.com/questions/2685804/scala-parameter-type-in-structural-refinement-may-not-refer-to-an-abstract-type – retronym

Répondre

10

Mon intuition serait que cela est dû en raison des énoncés suivants dans les spécifications, § 6.11, blocs:

Un type de définition type défini localement t = T est lié par la clause existentielle type t >: T <: T. C'est une erreur si t porte des paramètres de type.

et une expression de la création de l'instance de structure est évaluée à un bloc, de sorte


new {def greet{println("hello")}} 

est un raccourci pour


{ class anon$X extends AnyRef{ def greet = println("hello") }; new anon$X } 

de sorte qu'il évalue une expression de bloc (selon § 6,10 de la spécification), avec la restriction susmentionnée. Pourquoi cette restriction est là, je ne sais pas, cependant. L'erreur qui est levée peut être trouvée dans la classe Typers au this location, ce qui semble confirmer que cette restriction est la cause de l'erreur que vous voyez. Comme vous l'avez mentionné, codant pour la fonction dans une classe supprime la restriction de l'expression du bloc:


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} 

0

Pour moi, cela ressemble à une simplicité contre généralité cas: il pourrait y avoir une nouvelle variable de type généré chaque fois qu'un bloc est créé capture un constructeur de type instancié avec un type existentiel, mais cela rendrait le diagnostic d'erreur plus difficile à comprendre.

Notez également qu'ayant une classe transforme l'appel en un INVOKEVIRTUAL rapide, plutôt que d'appeler la méthode as() par réflexion.

Questions connexes