代码之家  ›  专栏  ›  技术社区  ›  Tom Crockett

“can't existentially abstract over parameterized type…”

  •  12
  • Tom Crockett  · 技术社区  · 14 年前

    I was messing around with Scala 2.8 for fun and trying to define a pimp 它向类型构造函数添加了一个“as”方法,允许从一个函数转换为另一个函数(请忽略我在这里不必处理函数这一事实)。例如,您可以这样使用它:

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

    So here's what I tried to do:

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

    however the definition of naturalTransformations is flagged with the error "can't existentially abstract over parameterized type G[T]" . 为了解决这个问题,我可以重写 自然转化 along with an additional class 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)
    

    2 回复  |  直到 14 年前
        1
  •  11
  •   Arjan Blokzijl    14 年前

    我的直觉是,这是因为规范中的以下陈述,第6.11条,块:

    A locally defined type definition type t = T is bound by the existential clause type t >: T <: T . It is an error if t carries type parameters.

    And a structural instance creation expression is evaluated to a block, so

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

    is a shorthand for

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

    因此,它评估了块表达式(根据规范的第6.10条),具有上述限制。然而,为什么我不知道这种限制。抛出的错误可以在Type类中找到。 this location , which seems to confirm that this restriction is the cause of the error that you see. As you mentioned, encoding the function in a class removes the block expression restriction:

    
    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
  •  0
  •   venechka    14 年前

    To me this sounds like a simplicity against generality case: there could be a new type variable generated every time a block is created capturing some type constructor instantiated with existential type, but that would make error diagnostics more difficult to understand.

    Also note that having a class turns the call into a fast INVOKEVIRTUAL, rather than invoking () method by reflection.