2012-10-08 12 views
7

I tipi di simboli class A[_] o di def a[_](x: Any) hanno un parametro di tipo che non può essere referenziato nel corpo, quindi non vedo dove è utile e perché compila. Se si cerca di fare riferimento a questo parametro di tipo, viene generato un errore:A cosa serve `classe 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 = _ } 
           ^

Qualcuno può dirmi se tale tipo ha un caso d'uso a Scala? Per essere precisi, non intendo tipi esistenziali o tipi più elevati nei parametri tipo, solo quelli litte [_] che formano l'elenco completo dei parametri di tipo.

risposta

4

Perché non ho avuto le risposte che mi aspettavo, ho portato questo per scala-language.

incollo qui la risposta da Lars Hupel (così, tutti i crediti si applicano a lui), il che spiega in gran parte quello che volevo sapere:

ho intenzione di dare una pugnalata qui. Penso che l'uso della funzione rimuova quando si parla di membri di tipo.

supponga che è necessario implementare la seguente caratteristica:

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

Questa sarebbe una funzione (generica), dove il tipo di ritorno dipende il tipo di ingresso. Un esempio per un'istanza:

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

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

Finora, tutto bene. Ora, come definiresti una funzione costante?

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

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

(Il tipo const0.Out[Int] è equivalente a Int, ma non è stampate in questo modo.)

noti come il parametro di tipo In non viene effettivamente utilizzato. Quindi, ecco come si potrebbe scrivere con _:

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

Pensate _ in questo caso come un nome per il parametro tipo che non può in realtà essere indicato.E 'una per una funzione sul livello di tipo, che non si preoccupa del parametro, proprio come sul valore livello:

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

Tranne ...

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

Confronti che, con:

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

Quindi, in conclusione:

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) 

La cosa principale che hai menzionato, class A[_], è completamente simmetrica a che, tranne che non esiste un vero caso d'uso.

Considerate questo:

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

Ora supporre si vuole fare un'istanza di FlyingDog per la pianura vecchio class A.

ci sono due soluzioni:

  1. Declare class A[_] invece. (Non farlo.)

  2. utilizzare un tipo lambda:

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

o anche

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

Questo è utile quando si gestiscono istanze di tipi parametrizzati senza preoccuparsi del parametro di 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

cosa si intende qui è un tipo esistenziale, che non è cosa intendevo. – sschaef

2

La sottolineatura in Scala indica un tipo esistenziale, vale a dire un parametro di tipo sconosciuto, che ha due utilizzo principale:

  • E 'utilizzato per i metodi che non si preoccupano il parametro di tipo
  • Viene utilizzato per i metodi in cui si desidera esprimere che un parametro di tipo è un costruttore di tipi.

Un costruttore di tipi è fondamentalmente qualcosa che necessita di un parametro di tipo per costruire un tipo concreto. Ad esempio puoi prendere la seguente firma.

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

Questa è una funzione che per alcuni CC[_], per esempio un List[_], crea un List[A] partire da una B e una funzione B=>A.

Perché sarebbe utile? Bene, si scopre che se si utilizza quel meccanismo insieme a impliciti e typeclass, è possibile ottenere quello che viene chiamato polimorfismo ad-hoc grazie al ragionamento del compilatore.

Immaginate per esempio di avere un certo tipo alto-kinded: Container[_] con una gerarchia di implementazioni concrete: BeautifulContainer[_], BigContainer[_], SmallContainer[_]. Per costruire un contenitore è necessario un

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

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

} 

Quindi, in pratica un ContainerBuilder è qualcosa che per un tipo specifico di container A [_] può costruire un A [B] con un B.

Mentre si tratterebbe utile? Ebbene si può immaginare che si potrebbe avere una funzione definita da qualche altra parte come la seguente:

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

E poi nel codice si potrebbe fare:

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

Infatti, il compilatore utilizzerà il rendimento richiesto type of myMethod per cercare un implicito che soddisfi i vincoli di tipo richiesti e genera un errore di compilazione se non c'è ContainerBuilder che soddisfa implicitamente i vincoli richiesti.

+2

Non intendevo tipi esistenziali o tipi più elevati come parametri di tipo, intendevo un parametro di tipo che esiste ma non può essere referenziato. – sschaef

+0

quindi la risposta giusta è la precedente – Edmondo1984

+0

quale precedente? Quello che è stato cancellato? – sschaef

4

ho avuto alcune idee casuali su ciò che potrebbe significare qui:

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

Oltre al caso d'uso banale, chiedendo al compilatore per fare un nome, perché io davvero non mi interessa (anche se forse io 'll nome in un secondo momento, quando ho implementare la classe), questa colpisce ancora di me come utile:

caso

un altro utilizzo è dove un tipo di parametro è deprecato perché miglioramenti nella inferenza di tipo rendono superfluo.

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

Quindi, ipoteticamente, si potrebbe mettere in guardia sull'uso di T[X, Y] ma non T[_, Y].

Anche se non è ovvio se l'annotazione verrebbe prima (valore parametro-stile) o dopo (annotazione su tipo stile).

[Edit: "perché si compila": case class Foo[_](i: Int) blocca ancora bene su 2.9.2]