2010-11-30 9 views
6

J'écris une classe qui sert de classe de base pour une série d'objets singleton. Dans chaque objet singleton, il y aura des vals représentant certaines propriétés, et je veux écrire une méthode qui, pour chaque objet singleton, n'accepte que des objets créés par lui.Comment utiliser les types d'objets singleton de Scala?

Je donne les résultats suivants:

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]) = {...} 
} 

Jusqu'à présent, si bon. Ensuite, je veux déclarer un de ces objets singleton:

object M extends Maker { 
    val a = make 
} 

Mais, si j'essaie ceci:

M.accept(M.a) 

je reçois une erreur de compilation:

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

Mon questions:

  1. Quel est le type object com.test.M, et comment est-il différent de com.test.M.type?
  2. Comment puis-je faire cela plus intelligemment?
+0

pour le point 2: il y a toujours la possibilité de faire 'Obj' une classe imbriquée de' Maker' et pour supprimer le paramètre de type, mais je ne veux pas Cela, puisque j'ai besoin de passer des instances Obj aux objets en dehors des classes dans mon exemple et j'ai besoin de filtrer sur le paramètre type. –

+1

Pourriez-vous s'il vous plaît fournir un exemple _compilable_? Quelque chose que je peux copier et coller dans un REPL? –

+0

Grande question: J'ai rencontré le même problème lors de l'implémentation d'un HList et le type de HNil a été déduit comme ** objet HNil ** et non ** HNil.type **. Amélioré à 2.9 nuit de construction et tout va bien maintenant. – raichoo

Répondre

16

Obtenez avec le temps, mon brave homme! J'ai corrigé cela il y a plus de 24 heures. Ensuite, je m'attends à voir des vélociraptors courir après des dodos, en train de craquer furieusement leurs fouets tout en regardant des citations boursières sur leurs écrans de veille pointés.

Le commit en question est: 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

Nice! Donc je suppose que c'était certainement un bug de compilateur plutôt qu'un problème avec ma représentation mentale des types en jeu. –

+0

Eh bien, personne n'a dit que c'était un bug. C'était un comportement indésirable, mais c'était comme spécifié. – extempore

+0

Assez juste, mais dans ce cas, pouvez-vous m'expliquer la différence entre le type 'objet M' et le type' M.type'? –

8

Utilisez this.type au lieu de M. Cet exemple simplifié devrait fonctionner:

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

Merci, cela fonctionne très bien. Mais pourquoi mon exemple ne marche-t-il pas, qu'est-ce qui ne va pas avec mon raisonnement? Quelle est exactement la différence entre 'object com.text.M' et' com.text.M.type'? –

2

Cela fonctionne:

La "sauce" secret utilise make[M.type] l'intérieur de l'objet singleton.

@retronym revient le mérite d'expliquer ceci: How to correctly type-annotate this HList?

+0

L'implicite est ici redondant: il ne fera que forcer M à être this.type ... dans ce cas, nous pourrions aussi bien utiliser this.type directement, nous obtenant la solution de Michel Krämer. –

+0

En fait, j'essaie d'éviter l'explicite '[M.type]' quand j'appelle 'make' de' M' ... –

+0

@Miles: J'ai juste supposé qu'il avait banalisé son vrai cas pour le poste mais que dans code réel, il l'exige – IttayD

3

Votre première question: « Quel est le type object com.test.M, et comment est-il différent de com.test.M.type? », N'a pas encore reçu de réponse. Je n'ai pas trouvé documenté dans la spécification, mais il semble que le type object M est le type interne représentant la classe implicitement créée lorsque vous définissez un objet M. Bien sûr, M est la seule instance de cette classe donc on s'attendrait à ce que le type object M soit équivalent à M.type, mais le compilateur ne le voit apparemment pas de cette façon. Le problème que vous rencontrez, en tant que @retronym explained, est que le type singleton M.type n'est pas inféré pour le paramètre type lorsque vous appelez votre méthode make. Ceci est pour la même raison que String est inférée plutôt que v.type dans la session ci-dessous:

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

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

identity est défini comme

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

Semble logique. Mais il semble que extempore ait corrigé ce comportement en 2.9, donc soit 'l'objet M' est vraiment' M.type' et il y avait un bogue, soit l'inférence de type a légèrement changé. –

+0

Ce qui m'a aussi intrigué si quand vous écrivez 'val v =" string "', v comme type String, et quand vous écrivez 'val singleton = M', alors singleton a le type' M.type' et pas 'object M' , donc je m'attendais à ce que mon exemple initial fonctionne. Heureux qu'il le fera en 2.9 –

Questions connexes