The first option is the most effective, the other is overhead by transferring the code to the function object. But it is certainly possible to create such a shell. Let 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 }
Pay attention to this.type , it says that the result of our functions is the type of the class in which they are defined. So now when we mix it with 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 use case for 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"); }
A chain call converts anything to this.type . However, it only works inside the class, you cannot call something like new MyClass2.methodOne(3).chain.methodTwo("x") outside.
Update: 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 = cx; // Usage: val r: MyClass = new Chain(new MyClass) { x.methodOne(1).methodTwo(2,3); }
source share