2010-11-30 25 views
6

Estoy escribiendo una clase que sirve como clase base para una serie de objetos singleton. En cada objeto singleton, habrá vals que representen ciertas propiedades, y deseo escribir un método que, para cada objeto singleton, solo acepte objetos creados por él.¿Cómo usar los tipos de objeto singleton de Scala?

Así que tienen la siguiente:

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

Hasta ahora, todo bien. Entonces quiero declarar una de estas simples objetos:

object M extends Maker { 
    val a = make 
} 

Pero entonces, si intento esto:

M.accept(M.a) 

entonces consigo un error en tiempo de compilación:

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

Mi preguntas:

  1. ¿Cuál es el tipo object com.test.M, y ¿cómo es diferente de com.test.M.type?
  2. ¿Cómo puedo hacer esto de una manera más inteligente?
+0

para el punto 2: siempre existe la posibilidad de hacer 'Obj' una clase anidada de' Maker' y para eliminar el parámetro de tipo, pero no quiero eso, dado que necesito pasar las instancias de Obj a objetos fuera de las clases en mi ejemplo y necesito filtrar el parámetro de tipo. –

+1

¿Podría proporcionar un ejemplo _compilable_? Algo que puedo copiar y pegar en un REPL? –

+0

Gran pregunta: me encontré con el mismo problema al implementar una HList y el tipo de HNil se infirió como ** objeto HNil ** y no ** HNil.type **. Actualizado a 2.9 build nocturno y todo está bien ahora. – raichoo

Respuesta

16

Obtener con los tiempos, buen hombre! Lo arreglé hace más de 24 horas. A continuación, espero ver velociraptors persiguiendo a dodos, rompiendo furiosamente sus látigos con errores mientras buscan cotizaciones de acciones en sus protectores de pantalla pointcast.

la confirmación de que se trate es: 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

¡Agradable! Así que supongo que definitivamente fue un error del compilador en lugar de un problema con mi representación mental de los tipos en juego. –

+0

Bueno, nadie dijo que era un error. Fue un comportamiento indeseable, pero fue como se especificó. – extempore

+0

Bastante, pero en este caso, ¿puede explicarme la diferencia entre el tipo 'objeto M' y tipo' M.type'? –

8

Use this.type en lugar de M. Este ejemplo simplificado debería funcionar:

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

Gracias, esto funciona bien. Pero, ¿por qué mi ejemplo no funciona? ¿Qué pasa con mi razonamiento? ¿Cuál es exactamente la diferencia entre 'object com.text.M' y' com.text.M.type'? –

2

esto funciona:

El secreto "salsa" está utilizando make[M.type] dentro del objeto Singleton.

@retronym merece el crédito para explicar esto: How to correctly type-annotate this HList?

+0

Lo implícito es redundante aquí: solo forzará a M a ser este tipo ... en cuyo caso también podríamos utilizar este tipo de letra directamente, obteniéndonos la solución de Michel Krämer. –

+0

En realidad, estoy tratando de evitar explícitamente '[M.type]' cuando llamo 'make' desde' M' ... –

+0

@Miles: Asumí que ha trivializado su caso real para la publicación, pero que en el código real lo requiere – IttayD

3

Su primera pregunta, "¿Cuál es el tipo object com.test.M, y cómo se diferencia de com.test.M.type?", Todavía no ha sido contestada. No he encontrado documentado en la especificación, pero parece que el tipo object M es el tipo interno que representa la clase que se crea implícitamente al definir un objeto M. Por supuesto, M es la única instancia de esa clase, por lo que cabría esperar que el tipo object M sea equivalente a M.type, pero aparentemente el compilador no lo ve de esa manera.

El problema que se está ejecutando en, como @retronym explained, es que el singleton tipo M.type no se infiere para el parámetro de tipo cuando se invoca el método make. Esto es por la misma razón que String se infiere en lugar de v.type en la sesión a continuación:

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

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

donde se define como identity

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

Tiene sentido. Pero parece que imprompore ha corregido este comportamiento en 2.9, por lo que o bien el 'objeto M' es realmente' M.type' y hubo un error, o la inferencia de tipo ha cambiado ligeramente. –

+0

Lo que también me desconcertó si escribes 'val v =" string "', v como tipo String, y cuando escribes 'val singleton = M', entonces singleton tiene type' M.type' y no 'object M' , así que esperaba que mi ejemplo inicial funcionara. Me alegro de que lo hará en 2.9 –

Cuestiones relacionadas