How can I programmatically make methods chainable?
scala
Solution
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);
}
Problem
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 = { println("doing stuff") this } def methodTwo(arg1: Any, arg2: Any): MyClass = { println("doing other stuff") this } } ``` While that would achieve the functionality I'm looking for, it's not very elegant in my opinion. Is there a better way of doing this? Assuming it's possible, I'd like to be able to do something like the following, but I'm not sure how to approach the `makeChainable` function. ``` class MyClass { val methodOne: Any => MyClass = makeChainable((arg1: Any) => println("doing stuff")) val methodTwo: (Any, Any) => MyClass = makeChainable((arg1: Any, arg2: Any) => println("doing other stuff")) } ```