2010-11-30 11 views
6

Piszę klasy, która służy jako klasa bazowa dla serii obiektów singleton. W każdym obiekcie singleton będą vals reprezentujące pewne właściwości i chcę napisać metodę, która dla każdego obiektu singleton akceptuje tylko obiekty przez nią utworzone.Jak korzystać z typów singleton-object Scala?

Więc mam następujące:

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

Tak daleko, tak dobrze. Następnie chcę zadeklarować jeden z tych pojedynczych obiektów:

object M extends Maker { 
    val a = make 
} 

ale wtedy, gdy próbuję to:

M.accept(M.a) 

następnie pojawia się błąd kompilacji:

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

My pytania:

  1. Jaki jest typ object com.test.M i czym się różni od com.test.M.type?
  2. Jak mogę to zrobić w bardziej inteligentny sposób?
+0

do punktu 2: zawsze istnieje możliwość, aby 'Obj' zagnieżdżona klasa' Maker' i usunąć parametr typu, ale nie chcę tak, ponieważ muszę przekazać instancje Obj do obiektów spoza klas w moim przykładzie i muszę filtrować na parametrze type. –

+1

Czy możesz podać przykład _compilable_? Coś, co mogę skopiować i wkleić do REPL? –

+0

Świetne pytanie: wpadłem na ten sam problem podczas implementacji HList, a typ HNil został wywnioskowany jako ** obiekt HNil **, a nie ** HNil.type **. Ulepszony do wersji 2.9 nightly i wszystko jest już w porządku. – raichoo

Odpowiedz

16

Get z duchem czasu, mój dobry człowieku! Naprawiłem to ponad 24 godziny temu. Następnie spodziewam się zobaczyć velociraptorów ścigających dodo, wściekle łamiących ich baty buggy, przeglądając notowania giełdowe na swoich ekranowych wygaszaczach ekranu.

popełnić w pytaniu: 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! Zakładam więc, że był to zdecydowanie błąd kompilatora, a nie problem z moją mentalną reprezentacją typów w grze. –

+0

Cóż, nikt nie powiedział, że to błąd. Było to niepożądane zachowanie, ale było tak, jak zostało to określone. – extempore

+0

Wystarczająco uczciwe, ale czy w tym przypadku można mi wytłumaczyć różnicę między typem "obiekt M" a typem "typ"? –

8

Użyj this.type zamiast M. Ten uproszczony przykład powinien działać:

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

Dzięki, to działa dobrze. Ale dlaczego mój przykład nie działa, co jest nie tak z moim rozumowaniem? Jaka jest dokładnie różnica między 'object com.text.M' a' com.text.M.type'? –

2

To działa:

Sekret "sos" używa make[M.type] wewnątrz obiektu singleton.

@retronym zasługuje na kredyt dla wyjaśnienia to: How to correctly type-annotate this HList?

+0

Ukryty tutaj jest zbędny: zmusi to M tylko do tego typu ... w takim przypadku możemy równie dobrze użyć tego .type bezpośrednio, dostarczając nam rozwiązania Michela Krämera. –

+0

Właściwie staram się unikać wyraźnego '[M.type]' podczas wywoływania 'make' z' M' ... –

+0

@Miles: Właśnie założyłem, że zbanalizował on swoją prawdziwą sprawę dla postu, ale to w rzeczywisty kod on wymaga tego – IttayD

3

Twoje pierwsze pytanie: „Jaki jest typ object com.test.M i czym różni się od com.test.M.type?”, Nadal nie zostało odebrane. Nie znalazłem tego udokumentowanego w specyfikacji, ale wygląda na to, że typ object M jest typem wewnętrznym reprezentującym klasę, która jest niejawnie tworzona podczas definiowania obiektu M. Oczywiście, M jest jedynym wystąpieniem tej klasy, więc można oczekiwać, że typ object M będzie równoważny z , ale kompilator najwyraźniej tego nie widzi.

Problem, który napotykamy, jako @retronym explained, polega na tym, że pojedynczy singleton typu M.type nie jest wywnioskowany dla parametru type podczas wywoływania metody make. To z tego samego powodu, że String jest wywnioskować zamiast v.type w poniższej sesji:

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

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

gdzie identity jest zdefiniowany jako

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

Ma sens. Wygląda jednak na to, że extempore naprawił to zachowanie w 2.9, więc albo 'object M' jest naprawdę' M.type' i pojawił się błąd, albo wnioskowanie o typie nieznacznie się zmieniło. –

+0

Co również mnie zaintrygowało, jeśli podczas pisania 'val v =" string "', v jako typ String, a gdy napiszesz 'val singleton = M', to singleton ma typ' M.type', a nie 'object M' , więc spodziewałem się, że mój pierwszy przykład zadziała. Cieszę się, że w 2.9 –