scalareflectionfunctional-programmingtype-inferencetwitter-util

dynamically parse a string and return a function in scala using reflection and interpretors


I am trying to dinamically interpret code given as a String. Eg:

val myString = "def f(x:Int):Int=x+1".

Im looking for a method that will return the real function out of it: Eg:

val myIncrementFunction = myDarkMagicFunctionThatWillBuildMyFunction(myString)
println(myIncrementFunction(3))

will print 4

Use case: I want to use some simple functions from that interpreted code later in my code. For example they can provide something like def fun(x: Int): Int = x + 1 as a string, then I use the interpreter to compile/execute that code and then I'd like to be able to use this fun(x) in a map for example.

The problem is that that function type is unknown for me, and this is one of the big problems because I need to cast back from IMain. I've read about reflection, type system and such, and after some googling I reached this point. Also I checked twitter's util-eval but I cant see too much from the docs and the examples in their tests, it's pretty the same thing.

If I know the type I can do something like

val settings = new Settings
val imain = new IMain(settings)
val res = imain.interpret("def f(x:Int):Int=x+1; val ret=f _ ")
val myF = imain.valueOfTerm("ret").get.asInstanceOf[Function[Int,Int]]
println(myF(2))

which works correctly and prints 3 but I am blocked by the problem I said above, that I dont know the type of the function, and this example works just because I casted to the type I used when I defined the string function for testing how IMain works.

Do you know any method how I could achieve this functionality ?

I'm a newbie so please excuse me if I wrote any mistakes.

Thanks


Solution

  • Ok, I managed to achieve the functionality I wanted, I am still looking for improving this code, but this snippet does what I want.

    I used scala toolbox and quasiquotes

    import scala.reflect.runtime.universe.{Quasiquote, runtimeMirror}
    import scala.tools.reflect.ToolBox
    
    object App {
        def main(args: Array[String]): Unit = {
            val mirror = runtimeMirror(getClass.getClassLoader)
            val tb = ToolBox(mirror).mkToolBox()
    
            val data = Array(1, 2, 3)
    
            println("Data before function applied on it")
            println(data.mkString(","))
    
    
            println("Please enter the map function you want:")
            val function = scala.io.StdIn.readLine()
            val functionWrapper = "object FunctionWrapper { " + function + "}"
            val functionSymbol = tb.define(tb.parse(functionWrapper).asInstanceOf[tb.u.ImplDef])
    
            // Map each element using user specified function
            val dataAfterFunctionApplied = data.map(x => tb.eval(q"$functionSymbol.function($x)"))
    
            println("Data after function applied on it")
            println(dataAfterFunctionApplied.mkString(","))
        }
    }
    

    And here is the result in the terminal:

    Data before function applied on it
    1,2,3
    Please enter the map function you want:
    def function(x: Int): Int = x + 2
    Data after function applied on it
    3,4,5
    
    Process finished with exit code 0