tribbloid
tribbloid

Reputation: 3848

In scala, how to find invocation of subroutines defined in a function

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

Answers (1)

Odomontois
Odomontois

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

Related Questions