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:
object com.test.M
, and how is it different from com.test.M.type
?Instead of static keyword Scala has singleton object. A Singleton object is an object which defines a single object of a class. A singleton object provides an entry point to your program execution. If you do not create a singleton object in your program, then your code compile successfully but does not give output.
Scala Singleton Object Singleton object is an object which is declared by using object keyword instead by class. No object is required to call methods declared inside singleton object. In scala, there is no static concept. So scala creates a singleton object to provide entry point for your program execution.
A singleton object named the same as a class is called a companion object. Also a companion object must be defined inside the same source file as the class.
A companion object is an object that's declared in the same file as a class , and has the same name as the class. A companion object and its class can access each other's private members. A companion object's apply method lets you create new instances of a class without using the new keyword.
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
%
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!
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
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With