2010-11-30 9 views
6

Sto scrivendo una classe che funge da classe base per una serie di oggetti singleton. In ogni oggetto singleton, ci saranno vals che rappresentano determinate proprietà, e voglio scrivere un metodo che, per ciascun oggetto singleton, accetta solo oggetti creati da esso.Come utilizzare i tipi di oggetti Singleton di Scala?

così ho il seguente:

class Obj[M <: Maker] 

class Maker { 
    implicit val me: this.type = this 
    def make[M <: Maker](implicit maker: M) = new Obj[M] 
    def accept(obj: Obj[this.type]) = {...} 
} 

Fin qui, tutto bene. Poi voglio dichiarare uno di questi oggetti Singleton:

object M extends Maker { 
    val a = make 
} 

Ma allora, se provo questo:

M.accept(M.a) 

allora ottengo un errore di compilazione:

type mismatch; found : com.test.Obj[object com.test.M] required: com.test.Obj[com.test.M.type] 

mio domande:

  1. Qual è il tipo object com.test.M e in che modo è diverso da com.test.M.type?
  2. Come posso farlo in un modo più intelligente?
+0

per il punto 2: c'è sempre la possibilità di fare 'Obj' una classe annidata di' Maker' e di togliere il parametro di tipo, ma non voglio questo, dato che ho bisogno di passare le istanze di Obj agli oggetti esterni alle classi nel mio esempio e ho bisogno di filtrare sul parametro type. –

+1

Potresti fornire un esempio _compilable? Qualcosa che posso copiare e incollare in un REPL? –

+0

Bellissima domanda: ho riscontrato lo stesso problema quando implementavo una lista HList e il tipo di HNil è stato inferito come ** oggetto HNil ** e non ** HNil.type **. Aggiornato a 2.9 nightly build e ora va tutto bene. – raichoo

risposta

16

Prendi con i tempi, buon uomo! Ho risolto questo problema oltre 24 ore fa. Poi mi aspetto di vedere i velociraptor che inseguono i dodos, rompendo furiosamente le loro fruste buggy mentre guardano le virgolette sui loro screensaver pointcast.

La impegnarsi in questione è: http://lampsvn.epfl.ch/trac/scala/changeset/23622

// 1130.scala 
class Obj[M <: Maker] 

class Maker { 
    implicit val me: this.type = this 
    def make[M <: Maker](implicit maker: M) = new Obj[M] 
    def accept(obj: Obj[this.type]) =() 
} 

object M extends Maker { 
    val a = make 
} 

object Test { 
    def main(args: Array[String]): Unit = { 
    M.accept(M.a) 
    } 
} 

// too old 
% /scala/inst/scala-2.9.0.r23619/bin/scalac ./1130.scala 
./1130.scala:15: error: type mismatch; 
found : Obj[object M] 
required: Obj[M.type] 
    M.accept(M.a) 
      ^
one error found 

// fresh enough 
% /scala/inst/scala-2.9.0.r23624/bin/scalac ./1130.scala 
% 
+0

Bello! Quindi presumo che fosse sicuramente un bug del compilatore piuttosto che un problema con la mia rappresentazione mentale dei tipi in gioco. –

+0

Beh, nessuno ha detto che era un bug. Era un comportamento indesiderabile, ma era come specificato. – extempore

+0

Abbastanza corretto, ma in questo caso, puoi spiegarmi la differenza tra tipo 'oggetto M' e tipo' M.type'? –

8

Utilizzare this.type anziché M. Questo esempio semplificato dovrebbe funzionare:

class Obj[M <: Maker] 

class Maker { 
    def make() = new Obj[this.type] 
    def accept(obj: Obj[this.type]) = println(obj) 
} 

object M extends Maker 

object N extends Maker 

M.accept(M.make()) //works! 
M.accept(N.make()) //error! type mismatch! 
+0

Grazie, funziona bene. Ma perché il mio esempio non funziona, cosa c'è di sbagliato nel mio ragionamento? Qual è esattamente la differenza tra 'oggetto com.text.M' e' com.text.M.type'? –

2

questo funziona:

La "salsa" segreto sta usando make[M.type] all'interno dell'oggetto Singleton.

@retronym merita il credito per spiegare questo: How to correctly type-annotate this HList?

+0

L'implicito è ridondante qui: costringerà sempre M a essere questo.tipo ... nel qual caso potremmo usare direttamente questo.tipo, ottenendo la soluzione di Michel Krämer. –

+0

In realtà io sto cercando duramente per evitare l'esplicito '[M.type]' quando si chiama '' make' da M' ... –

+0

@Miles: Ho appena assunto che ha banalizzato il suo vero caso per il posto ma che in codice effettivo che lo richiede – IttayD

3

tua prima domanda, "Qual è il tipo di object com.test.M, e come è diverso da com.test.M.type?", Ancora non è stato risposto. Non l'ho trovato documentato nelle specifiche, ma sembra che il tipo object M sia il tipo interno che rappresenta la classe che viene creata implicitamente quando si definisce un oggetto M. Ovviamente, M è l'unica istanza di quella classe, quindi ci si aspetterebbe che il tipo object M sia equivalente a M.type, ma il compilatore apparentemente non lo vede in questo modo.

Il problema si sta eseguendo in, come @retronym explained, è che il tipo di Singleton M.type non è deducibile per il parametro di tipo quando si richiama il metodo make. Questo è per la stessa ragione che String si evince piuttosto che v.type nella sessione di seguito:

scala> val v = "asdf"      
v: java.lang.String = asdf 

scala> identity(v) 
res0: java.lang.String = asdf 

dove identity è definito come

def identity[T](v: T) = v 
+0

ha senso. Ma sembra che extempore abbia corretto questo comportamento in 2.9, quindi "oggetto M" è in realtà "M.type" e c'era un bug, oppure l'inferenza di tipo è leggermente cambiata. –

+0

Quello che mi ha anche sconcertato se che quando si scrive 'val v = "string"', v come tipo String, e quando si scrive 'val Singleton = M', quindi Singleton è di tipo' M.type' e non 'M' oggetto , quindi mi aspettavo che il mio esempio iniziale funzionasse. Li ringrazio in 2.9 –

Problemi correlati