Domanda "Il tipo di parametro nel raffinamento strutturale non può riferirsi a un tipo astratto definito al di fuori di tale raffinamento"


Quando compilo:

object Test extends App {
  implicit def pimp[V](xs: Seq[V]) = new {
    def dummy(x: V) = x
  }
}                                                                                                                                                                                                              

Ottengo:

$ fsc -d aoeu go.scala
go.scala:3: error: Parameter type in structural refinement may not refer to an abstract type defined outside that refinement
    def dummy(x: V) = x
        ^
one error found

Perché?

(Scala: "Il tipo di parametro nel raffinamento strutturale non può riferirsi a un tipo astratto definito al di fuori di tale raffinamento" in realtà non risponde a questo.)


24
2017-10-20 03:15


origine


risposte:


Non è consentito dalle specifiche. Vedere 3.2.7 Tipi composti.

All'interno di una dichiarazione di metodo in un riferimento strutturale, il tipo di qualsiasi parametro di valore può riferirsi solo ai parametri di tipo o ai tipi astratti contenuti nel dettaglio. Cioè, deve fare riferimento a un parametro di tipo del metodo   stesso, o ad una definizione di tipo all'interno del raffinamento. Questa restrizione non si applica   al tipo di risultato della funzione.

Prima Bug 1906 è stato corretto, il compilatore avrebbe compilato questo e avresti ottenuto un metodo non trovato in fase di esecuzione. Questo è stato risolto in revisione 19442 ed è per questo che ricevi questo meraviglioso messaggio.

La domanda è quindi, perché non è permesso?

Ecco molto spiegazione dettagliata da Gilles Dubochet dalla mailing list scala nel 2007. Si riduce in gran parte al fatto che i tipi strutturali usano la riflessione e il compilatore non sa come cercare il metodo da chiamare se usa un tipo definito al di fuori della raffinatezza (il compilatore non sa in anticipo come riempire il secondo parametro di getMethod in p.getClass.getMethod("pimp", Array(?))

Ma guarda il post, risponderà alla tua domanda e altro ancora.

Modificare:

Ciao lista.

Provo a definire i tipi strutturali con il tipo di dati astratto in funzione   parametro. ... Qualche ragione?

Ho sentito parlare di due domande riguardanti la tipizzazione strutturale estensione di Scala 2.6 ultimamente, e vorrei rispondere qui.

  1. Perché abbiamo modificato lo schema di box dei valori nativi di Scala ("int", ecc.) a Java ("java.lang.Integer").
  2. Perché la restrizione sui parametri è strutturalmente definita metodi ("Il tipo di parametro nel raffinamento strutturale potrebbe non riferirsi per astrarre il tipo definito al di fuori di quello stesso raffinamento ") richiesto.

Prima di poter rispondere a queste due domande, ho bisogno di parlare del implementazione di tipi strutturali.

Il sistema di tipi della JVM è molto semplice (e corrisponde a Java 1.4). Quella significa che molti tipi che possono essere rappresentati in Scala non possono essere rappresentato nella VM. Tipi dipendenti dal percorso ("x.y.A"), tipi singleton ("A.type"), tipi composti ("A con B") o tipi astratti sono tutti i tipi che non può essere rappresentato nel sistema di tipi della JVM.

Per essere in grado di compilare il bytecode JVM, i compilatori Scala cambiano il I tipi di Scala del programma alla loro "cancellazione" (vedi la sezione 3.6 del riferimento). I tipi cancellati possono essere rappresentati nel sistema di tipi della VM e definire una disciplina di tipo sul programma che è equivalente a quella di il programma digitato con tipi Scala (salvando alcuni calchi), sebbene meno preciso. Come nota a margine, il fatto che i tipi vengono cancellati nella VM spiega perché le operazioni sulla rappresentazione dinamica dei tipi (modello matching sui tipi) sono molto limitati rispetto al tipo di Scala sistema.

Fino ad ora tutti i costrutti di tipo in Scala potevano essere cancellati in qualche modo. Questo non è vero per i tipi strutturali. Il tipo strutturale semplice "{def x: Int} "non può essere cancellato su" Oggetto "come la VM non consentirebbe accedere al campo "x". Utilizzando un'interfaccia "interfaccia X {int x {}; }” poiché il tipo cancellato non funzionerà neanche perché ogni istanza è vincolata da a il valore di questo tipo dovrebbe implementare quell'interfaccia che non può essere fatto in presenza di una compilazione separata. Infatti (sopportare con me) qualsiasi classe che contiene un membro con lo stesso nome di un membro definito in un tipo strutturale dovunque dovrebbe implementare il corrispondente interfaccia. Sfortunatamente questa classe può essere definita anche prima del è noto che il tipo strutturale esiste.

Invece, qualsiasi riferimento a un membro strutturalmente definito è implementato come una chiamata riflessiva, bypassando completamente il sistema di tipo della VM. Per esempio def f(p: { def x(q: Int): Int }) = p.x(4) sarà riscritto a qualcosa di simile:

  def f(p: Object) = p.getClass.getMethod("x", Array(Int)).invoke(p, Array(4))

E ora le risposte.

  1. "Invoke" utilizzerà i valori boxed ("java.lang.Integer") ogni volta che il metodo invocato usa valori nativi ("int"). Ciò significa che quanto sopra la chiamata deve davvero assomigliare a "... invoke (p, Array (new java.lang.Integer (4))). intValue”.

I valori interi in un programma Scala sono già spesso in scatola (per consentire il "Qualsiasi" tipo) e sarebbe inutile separarli da quelli di Scala schema di boxe per reinserirli immediatamente come java.lang.Integer.

Peggio ancora, quando una chiamata riflessiva ha il tipo di ritorno "Qualsiasi", cosa dovrebbe essere fatto quando viene restituito un java.lang.Integer? Il chiamato il metodo potrebbe restituire un "int" (nel qual caso dovrebbe essere unboxed e reboxed come una casella di Scala) o potrebbe restituire a java.lang.Integer che dovrebbe essere lasciato intatto.

Invece abbiamo deciso di modificare lo schema di boxe di Scala in Java. Il due problemi precedenti poi semplicemente scompaiono. Alcune prestazioni correlate ottimizzazioni che abbiamo avuto con lo schema di boxe di Scala (pre-calcolare il forma in scatola dei numeri più comuni) erano facili da usare con Java boxe anche. Alla fine, usare la boxe Java era anche un po 'più veloce di il nostro schema.

  1. Il secondo parametro di "getMethod" è un array con i tipi di parametri del metodo (strutturalmente definito) per cercare - per selezionare quale metodo ottenere quando il nome è sovraccarico. Questo è il un posto in cui sono necessari i tipi esatti e statici nel processo di traduzione di una chiamata membro strutturale. Solitamente, tipi statici sfruttabili perché i parametri di un metodo sono forniti con il tipo strutturale definizione. Nell'esempio sopra, il tipo di parametro "x" è noto a essere "Int", che consente di cercarlo.

Tipi di parametri definiti come tipi astratti in cui si trova il tipo astratto definito all'interno della portata della raffinatezza strutturale non sono un problema o:   def f (p: {def x [T] (t: T): Int}) = p.xInt In questo esempio sappiamo che ogni istanza passerà a "f" come "p" lo farà definire "x [T] (t: T)" che è necessariamente cancellato a "x (t: Object)". Il la ricerca viene eseguita correttamente sul tipo cancellato:   def f (p: Object) = p.getClass.getMethod ("x", Array (Object)). invoke (p, Array (new java.lang.Integer (4)))

Ma se un tipo astratto è al di fuori dell'ambito del raffinamento strutturale è usato per definire un parametro di un metodo strutturale, tutto si spezza:   def f [T] (p: {def x (t: T): Int}, t: T) = p.x (t) Quando viene chiamata "f", "T" può essere istanziato a qualsiasi tipo, ad esempio:   f [Int] ({def x (t: Int) = t}, 4)   f [Qualsiasi] ({def x (t: Any) = 5}, 4) La ricerca per il primo caso dovrebbe essere "getMethod (" x ", Array (int)) "e per il secondo" getMethod ("x", Array (Object)) ", e non c'è modo di sapere quale generare nel corpo di "F": "p.x (t)".

Per consentire la definizione di una chiamata "getMethod" unica all'interno del corpo di "f" per qualsiasi istanziazione di "T" richiederebbe qualsiasi oggetto passato a "f" come il Parametro "p" per avere il tipo di "t" cancellato su "Qualsiasi". Questo sarebbe a trasformazione in cui il tipo di membri di una classe dipende da come istanze di questa classe sono usate nel programma. E questo è qualcosa sicuramente non vogliamo fare (e non può essere fatto con separato compilazione).

In alternativa, se Scala supporta i tipi di runtime a cui potrebbero essere utilizzati risolvi questo problema. Forse un giorno ...

Ma per ora, usando i tipi astratti per il parametro del metodo strutturale i tipi è semplicemente proibito.

Cordiali saluti, Gilles.


22
2017-10-20 12:50



Scoperto il problema poco dopo la pubblicazione di questo: Devo definire una classe denominata invece di usare una classe anonima. (Mi piacerebbe comunque sentire una spiegazione migliore del ragionamento).

object Test extends App {
  case class G[V](xs: Seq[V]) {
    def dummy(x: V) = x
  }
  implicit def pimp[V](xs: Seq[V]) = G(xs)
}                                                                                                                                                                                                              

lavori.


11
2017-10-20 03:19