this.type is a singleton type, which means the type represents a certain object, not class of objects. It helps to solve the problem when you need to chain method calls of a class hierarchy, i.e. "value.method1.method2", where method1 is in super class A and method2 is in deriving class B.
class A {def method1: A = this }Now we have defined the hierarchy, and created an instance of B named 'b'. Let's see about the chaining of method calls: The following works fine, because method2 returns type B which has method1:
class B extends A (def method2: B = this}
val b = new B
b.method2.method1However, this won't
b.method1.method2because method1's result type is A, and so compiler thinks that the object hasn't got the method2.
We could solve this by overriding the method1 in B, which calls the super classes' method1 to do whatever work it does. The result type is covariantly set as B:
class B extends A{But this is extra work for every subclass of A that needs method chaining!
override def method1: B = {super.method1; this };
...
}
Here's the other way we can do it in Scala: Using this.type we can tell compiler that the resulting object from method1 is exactly the same as b, so compiler can infer that 'yeah, the resulting object from method1 has method2 too'.
class A { def method1: this.type = this }Now
class B extends A { def method2: this.type = this }
// in method2 this.type is not a necessity
// unless there's subclass of B.
val b = new Bworks with both alternatives.
b.method1.method2
For more specific information you can see: Scalable Component Abstractions which is a very good read otherwise, too.
Scala has also linear mixin composition of which you can read also on the same link. I'm not going into details, but idea is to have partial implementations as traits which we mixin together. We use them in this article, because we want to create a library, but we are not sure how we'd like to extend it later and want to take only the parts we need. Additionally we want the chaining work with all the upcoming traits uniformly, not just with the methods in the superclass. This is achieved with traits and this.type.
The heart is CommandCenter, which queues up given computations, which are just normal Scala functions, of type Unit => Any.
trait CommandCenter{Now we can make objects of CommandCenter that can chain adding of computations, e.g.
protected var queue = List[Unit => Any]()
def <+(computation: => Any): this.type = {
queue :::= List[Unit=>Any]({x => computation})
this
}
}
val cmd = new CommandCenter{}Certainly we'd like to execute those computations later, but we don't want to pollute the same trait, because it's different responsibility. Here's first a helper class, which just forces the evalution of computations that are in the list.
cmd <+ Console.println("hi") <+ {network.send("bye")}
def executeAll(list: List[Unit => Any])Here's the trait for actual execution:
= list foreach {_()}
trait ExecuteCommands extends CommandCenter{Now we'd like to extend this thing so that we can make a group of computations and give them names by which we can later ask them to be evaluated.
def execute: Unit = executeAll(queue)
}
trait GroupedCommands extends CommandCenter {'>>' method ends the construction of computations; it takes all the computations in the queue and names it as a group, and clears the original queue so that new computations can be binded together.
import scala.collection.mutable.HashMap
protected val groups = HashMap[String, List[Unit=>Any]]()
def >>(name: String) = {
groups += ((name, queue))
queue = List()
}
}
We have also different trait for execution group computations, but we skip that (you can see it in the complete source code).
Now we can create a test case. First we have a construct method that only creates the computations. We restrict ourself from knowing more than grouping build operations i.e. GroupedCommands trait.
def construct(command: GroupedCommands) = {Of course <+ is kinda useless, we could just make a one big computation with {comp1; comp2; ... compN}, but this is only an example, though by partioning them to be differentiable, one could use for example time delay between evaluations. Here's the execute part:
var (x, y) = (1.0, 1.0)
def printx = Console.println("x: " + x)
command <+ printx <+ { x *= 2 } <+ printx >> "doubleX"
command <+ printx <+ { x /= 2 } <+ printx >> "halfX"
}
def execute(command: GroupExecution) = {And finally the test that gives observable results:
command execute("doubleX")
command execute("doubleX")
command execute("halfX")
}
val command = new CommandCenterIt prints:
with GroupedCommands with GroupExecution
construct(command)
execute(command)
x: 1.0, x: 2.0, x: 2.0, 4.0, x: 4.0, x: 2.0Here's the complete source code.
So now you know of this.type. Spread the word, for rarely anyone mentions it.
11 comments:
We briefly mention singleton types in http://programming.scala.com, but I wish we had thought of your examples! Great work.
Oops. Make that http://programmingscala.com. Sorry.
Your source code link is broken; I get a 403.
Thanks for the explanation :) appreciate your time on this
nice example, good explanation, glad I somehow came across this article! good job
nice example, good explanation, glad I somehow came across this article! good job
nice example, good explanation, glad I somehow came across this article! good job
Thanks for sharing this blog post,Nice written skill Java online training Bangalore
nice blog.thank you for sharing post
web programming tutorial
welookups
class 9 tution classes in gurgaon
Post a Comment