Let\'s say I have a class and I want to make its methods chainable, I could do something like this:
class MyClass {
def methodOne(arg1: Any): MyClass = {
The first option is the most efficient one, the other one introduces overhead by wrapping code into function object. But it's certainly possible to create such a wrapper. Let's define
trait Chainable {
final def mkChain(f: () => Any): () => this.type =
() => { f(); this; }
final def mkChain[A](f: (A) => Any): (A) => this.type =
(x: A) => { f(x); this; }
final def mkChain[A,B](f: (A,B) => Any): (A,B) => this.type =
(x: A, y: B) => { f(x, y); this; }
// etc. for other arities
}
Notice this.type, it says the result of our functions is the type of the class they're defined in. So now when we mix it into our class
class MyClass extends Chainable {
val methodTwo =
mkChain((x: Any, y: String) => println("Doing something " + y));
}
the result of methodTwo will be MyClass.
Update: There is another option, to use implicit conversions:
trait ToChain {
implicit class AsThis(val _underlying: Any) {
def chain: ToChain.this.type = ToChain.this
}
}
class MyClass2 extends ToChain {
def methodOne(arg1: Any): Unit =
println("Doing something")
def methodTwo(arg1: String): Unit =
println("Doing something else " + arg1)
methodOne(3).chain.methodTwo("x");
}
Calling chain converts anything to this.type. However it only works inside the class, you can't call something like new MyClass2.methodOne(3).chain.methodTwo("x") outside.
Update: Yet another solution, based on implicit conversion from Unit to this:
import scala.language.implicitConversions
class Chain[A](val x: A) {
implicit def unitToThis(unit: Unit): A = x;
}
implicit def unchain[A](c: Chain[A]): A = c.x;
// Usage:
val r: MyClass = new Chain(new MyClass) {
x.methodOne(1).methodTwo(2,3);
}