2012-10-08 14 views
7

Los tipos de símbolos class A[_] o de def a[_](x: Any) tienen un parámetro de tipo que no se puede referenciar en el cuerpo, por lo tanto, no veo dónde es útil y por qué se compila. Si uno trata de hacer referencia a este tipo de parámetro, se genera un error:¿Para qué sirve la clase A [_]?

scala> class A[_] { type X = _ } 
<console>:1: error: unbound wildcard type 
     class A[_] { type X = _ } 
          ^

scala> def a[_](x: Any) { type X = _ } 
<console>:1: error: unbound wildcard type 
     def a[_](x: Any) { type X = _ } 
           ^

Puede alguien decirme si tal tipo tiene un caso de uso en Scala? Para ser exactos, no me refiero a los tipos existenciales ni a los tipos con mayor grado de afinidad en los parámetros de tipo, solo aquellos en los que [_] forman la lista completa de parámetros de tipos.

Respuesta

4

Debido a que no he tenido las respuestas que esperaba, me trajo a scala-language.

pego aquí la respuesta de Lars Hupel (así, todos los créditos se aplican a él), que en su mayoría se explica lo que quería saber:

Voy a darle una puñalada aquí. Creo que el uso de la función es claro cuando se habla de miembros de tipo.

Supongamos que usted tiene que poner en práctica los siguientes rasgos:

trait Function { 
    type Out[In] 
    def apply[In](x: In): Out[In] 
} 

Esta sería una función (genérico), donde el tipo de retorno depende de el tipo de entrada. Un ejemplo para una instancia:

val someify = new Function { 
    type Out[In] = Option[In] def 
    apply[In](x: In) = Some(x) 
} 

someify(3) res0: Some[Int] = Some(3) 

Hasta ahora, todo bien. Ahora, ¿cómo definirías una función constante?

val const0 = new Function { 
    type Out[In] = Int 
    def apply[In](x: In) = 0 
} 

const0(3) res1: const0.Out[Int] = 0 

(El tipo const0.Out[Int] es equivalente a Int, pero no es impresa de esa manera.)

Nota cómo no se utiliza realmente el parámetro de tipo In. Por lo tanto, aquí es cómo se podría escribir con _:

val const0 = new Function { 
    type Out[_] = Int 
    def apply[In](x: In) = 0 
} 

Piense en _ en ese caso como un nombre para el parámetro de tipo que realidad no se puede hacer referencia a.Es una para una función en el nivel de tipo que no se preocupan por el parámetro, al igual que en el nivel de valor :

(_: Int) => 3 res4: Int => Int = <function1> 

Excepto ...

type Foo[_, _] = Int 
<console>:7: error: _ is already defined as type _ 
     type Foo[_, _] = Int 

compararlo con:

(_: Int, _: String) => 3 res6: (Int, String) => Int = <function2> 

Por lo tanto, en conclusión:

type F[_] = ConstType // when you have to implement a type member def 
foo[_](...) // when you have to implement a generic method but don't 
      // actually refer to the type parameter (occurs very rarely) 

Lo principal que mencionó, class A[_], es completamente simétrico al que, excepto que no existe un caso de uso real.

Considera:

trait FlyingDog[F[_]] { def swoosh[A, B](f: A => B, a: F[A]): F[B] } 

Ahora supuesto de que desea hacer una instancia de FlyingDog para su llanura de edad class A.

hay dos soluciones:

  1. Declarar class A[_] lugar. (No haga eso.)

  2. utilizar un tipo lambda:

    new FlyingDog[({ type λ[α] = A })#λ] 
    

o incluso

new FlyingDog[({ type λ[_] = A })#λ] 
1

Esto es útil cuando se trata de instancias de tipos parametrizados sin importar el parámetro tipo.

trait Something[A] { 
    def stringify: String 
} 

class Foo extends Something[Bar] { 
    def stringify = "hop" 
} 

object App { 
    def useSomething(thing: Something[_]) :String = { 
    thing.stringify 
    } 
} 
+1

Lo que quiere decir aquí es un tipo existencial, que no es Lo que quise decir. – sschaef

2

El subrayado en Scala indica un tipo existencial, es decir, un parámetro de tipo desconocido, que tiene dos uso principal:

  • Se utiliza para métodos que no se preocupan por el parámetro de tipo
  • Se utiliza para los métodos en los que desea expresar que un parámetro de tipo es un constructor de tipo.

Un constructor de tipos es básicamente algo que necesita un parámetro de tipo para construir un tipo concreto. Por ejemplo, puede tomar la siguiente firma.

def strangeStuff[CC[_], B, A](b:B, f: B=>A): CC[A] 

Esta es una función que por alguna CC[_], por ejemplo un List[_], crea una List[A] a partir de una B y una función B=>A.

¿Por qué sería útil? Bueno, resulta que si usas ese mecanismo junto con implicits y clases de tipos, puedes obtener lo que se llama polimorfismo ad-hoc gracias al razonamiento del compilador.

Imagine por ejemplo, usted tiene algún tipo kinded superior: Container[_] con una jerarquía de implementaciones concretas: BeautifulContainer[_], BigContainer[_], SmallContainer[_]. Para construir un contenedor que necesita un

trait ContainerBuilder[A[_]<:Container[_],B] { 

def build(b:B):A[B] 

} 

Así que, básicamente, un ContainerBuilder es algo que para un tipo específico de envase A [_] puede construir una A [B] utilizando un B.

Si bien habría que útil ? Bien se puede imaginar que podría tener una función definida en otro lugar como la siguiente:

def myMethod(b:B)(implicit containerBuilder:ContainerBuilder[A[_],B]):A[B] = containerBuilder.build(b) 

Y luego, en su código que podría hacer:

val b = new B() 
val bigContainer:BigContainer[B] = myMethod(b) 
val beautifulContainer:BeautifulContainer[B] = myMethod(b) 

De hecho, el compilador utilizará el rendimiento requerido tipo de myMethod para buscar un implícito que satisfaga las restricciones de tipo requeridas y lanzará un error de compilación si no hay ContainerBuilder que cumpla con las restricciones requeridas implícitamente.

+2

No quise decir tipos existenciales o tipos de kinded más altos como parámetros de tipo, quise decir un parámetro de tipo que existe pero no se puede referenciar. – sschaef

+0

entonces la respuesta correcta es la anterior – Edmondo1984

+0

que la anterior? ¿El que ha sido eliminado? – sschaef

4

que tenían algunas ideas casuales acerca de lo que podría significar aquí:

https://issues.scala-lang.org/browse/SI-5606

Además del caso de uso trivial, pidiendo el compilador para compensar un nombre porque realmente no me importa (aunque tal vez 'll nombrar más tarde cuando implemente la clase), ésta todavía me parece útil:

caso

Otro uso es donde un parámetro de tipo está en desuso porque mejoras en la inferencia de tipos hacen que sea superfluo.

trait T[@deprecated("I'm free","2.11") _, B <: S[_]] 

Entonces, hipotéticamente, se podría advertir sobre el uso de T[X, Y] pero no T[_, Y].

Aunque no es obvio si la anotación vendría antes (valor de estilo de parámetro) o después (anotación en tipo de estilo).

[Editar: "¿por qué se compila": case class Foo[_](i: Int) vuelve a colgarse muy bien en 2.9.2]