Reputation: 3848
Assuming that I have a function/method:
class A {
def function() = {
subroutine("A")
...
subroutine("B")
...
subroutine("C")
}
def subroutine(a: String) = { ... }
}
Is it possible to use Scala reflective programming to find all 3 invocations of subroutine(a: String) in function(), without calling function() itself? (Which will potentially take a long process)
Upvotes: 2
Views: 190
Reputation: 16318
You can try to resolve this problem in general without introspection via some cool functional tools.
Let's use scalaz library it has special typeclass Arrow
which is abstraction of Function
.
It allows to do pretty much anything that you usualy do with functions.
So, lets define special type which contains not only function, but call hierarchy in some readable way.
import scalaz._
import scalaz.syntax.tree._
import scalaz.std.function._
import scalaz.syntax.arrow._
import scalaz.std.string._
case class Subroutine[-A, +B](hier: Seq[Tree[String]], run: A => B) {
def named(name: String) = Subroutine(Seq(name.node(hier: _*)), run)
def printHier = hier.map(_.drawTree).mkString("\n" + "V" * 15 + "\n")
}
object Subroutine {
def named[A, B](tag: String)(run: A => B) = Subroutine(Seq(tag.leaf), run)
implicit def anon[A, B](run: A => B) = Subroutine(Seq.empty, run)
implicit object subroutineArrow extends Arrow[Subroutine] {
def arr[A, B](f: (A) => B): Subroutine[A, B] = anon(f)
def first[A, B, C](f: Subroutine[A, B]): Subroutine[(A, C), (B, C)] =
Subroutine(f.hier, f.run.first[C]).named("$1->")
override def second[A, B, C](f: Subroutine[A, B]): Subroutine[(C, A), (C, B)] =
Subroutine(f.hier, f.run.second[C]).named("$2->")
def id[A]: Subroutine[A, A] = anon(identity)
def compose[A, B, C](f: Subroutine[B, C], g: Subroutine[A, B]): Subroutine[A, C] =
Subroutine(g.hier ++ f.hier, f.run compose g.run)
}
}
Now lets define some subroutines
import Subroutine._
val square = { (x: Double) => x * x } named "square"
val sqrt = math.sqrt _ named "sqrt"
val sum = Subroutine.named[(Double, Double), Double]("sum"){ case (x, y) => x + y}
val abs = ((square *** square) >>> sum >>> sqrt) named "abs"
from here you can verify that
abs.run(3,4)
giving result 5.0
while
abs.printHier
giving interesting calling order like
"abs"
|
+- "$1->"
| |
| `- "square"
|
+- "$2->"
| |
| `- "square"
|
+- "sum"
|
`- "sqrt"
or even define
def pack22[X] = Subroutine.anon[(X, X, X, X), ((X, X), (X, X))] { case (a, b, c, d) => ((a, b), (c, d)) }
val abs4 = ((abs *** abs) >>> abs <<< pack22[Double]) named "abs4"
and evaluate
abs4.run(15, 20, 36, 48)
and
abs4.printHier
Upvotes: 1