2010-06-26 13 views
11

Estaba jugando con Scala 2.8 por diversión y tratando de definir un pimp que agrega un método "como" para escribir constructores, lo que permite convertir de un funtor a otro (Por favor, pase por alto el hecho de que no necesariamente estoy tratando con funtores aquí). Así, por ejemplo, se puede utilizar de esta manera:"no puede existir existencialmente sobre el tipo parametrizado ..."

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

Así que aquí es lo que he intentado hacer:

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

sin embargo la definición de naturalTransformations se marca con el error "no puede existencialmente abstracta sobre el tipo parametrizado G [T] ". Para solucionar este problema, puedo reescribir naturalTransformations junto con una clase adicional Transformable así:

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) 

y parece trabajar. Pero parece que mi primer intento debería haber sido equivalente, entonces tengo curiosidad por qué falló y qué significa el mensaje de error.

+1

Estoy acostumbrado a ver el error "El tipo de parámetro en el refinamiento estructural puede no referirse a un tipo de resumen definido fuera de ese refinamiento" en situaciones similares. Esa restricción está relacionada con la forma en que los Tipos Estructurales se implementan en la JVM con reflexión, IIRC. http://stackoverflow.com/questions/2685804/scala-parameter-type-in-structural-refinement-may-not-refer-to-an-abstract-type – retronym

Respuesta

10

Mi impresión sería que esto se debe a que, debido a las siguientes declaraciones en la especificación, § 6.11, bloques:

Un tipo definido localmente definición de tipo t = T está obligado por el existencial cláusula tipo T >: T <: T. Es un error si t lleva parámetros de tipo.

y una expresión de creación de la instancia estructural se evalúa a un bloque, por lo


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

es una abreviatura para


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

por lo que se evalúa como una expresión de bloque (de acuerdo con § 6,10 de la especificación), con la restricción mencionada anteriormente. Por qué esta restricción está ahí, no lo sé, sin embargo. El error que se produce se puede encontrar en la clase de Typers en this location, que parece confirmar que esta restricción es la causa del error que ve. Como usted ha mencionado, la codificación de la función en una clase elimina la restricción expresión de bloque:


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

Para mí esto suena como una simplicidad contra la caja generalidad: podría haber una nueva variable de tipo generado cada vez que un bloque se crea captura algún tipo constructor instanciado con tipo existencial, pero eso haría el diagnóstico de error más difícil de entender.

También tenga en cuenta que el tener una clase convierte la llamada en una INVOKEVIRTUAL rápido, en vez de invocar como ( método) por la reflexión.

Cuestiones relacionadas