问题
I'm writing a class which serves as base class for a series of singleton objects. In each singleton objects, there will be vals representing certain properties, and I want to write a method which, for each singleton object, only accepts objects created by it.
So I have the following:
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]) = {...}
}
So far, so good. Then I want to declare one of these singleton objects:
object M extends Maker {
val a = make
}
But then, if I try this:
M.accept(M.a)
then I get a compile-time error:
type mismatch; found : com.test.Obj[object com.test.M] required: com.test.Obj[com.test.M.type]
My questions:
- What's the type
object com.test.M
, and how is it different fromcom.test.M.type
? - How can I do this in a smarter way?
回答1:
Get with the times, my good man! I fixed this over 24 hours ago. Next I expect to see velociraptors chasing dodos, furiously cracking their buggy whips while looking up stock quotes on their pointcast screensavers.
The commit in question is: 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
%
回答2:
Use this.type
instead of M
. This simplified example should work:
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!
回答3:
Your first question, "What's the type object com.test.M
, and how is it different from com.test.M.type
?", still hasn't been answered. I haven't found it documented in the spec, but it seems that the object M
type is the internal type representing the class that is implicitly created when you define an object M
. Of course, M
is the only instance of that class so one would expect the object M
type to be equivalent to M.type
, but the compiler apparently does not see it that way.
The problem you're running into, as @retronym explained, is that the singleton type M.type
is not inferred for the type parameter when you invoke your make
method. This is for the same reason that String
is inferred rather than v.type
in the session below:
scala> val v = "asdf"
v: java.lang.String = asdf
scala> identity(v)
res0: java.lang.String = asdf
where identity
is defined as
def identity[T](v: T) = v
回答4:
This works:
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[M.type]
}
M.accept(M.a)
The secret "sauce" is using make[M.type]
inside the singleton object.
@retronym deserves the credit for explaining this: How to correctly type-annotate this HList?
来源:https://stackoverflow.com/questions/4315678/how-to-use-scalas-singleton-object-types