Search code examples
scalamethodsdynamicruntimeinvocation

How to call method based on dynamic input value at run time in scala?


Scenario is as follows :

Main class reads inputs values (e.g. A, B, C, D) from a LIST iteratively.

Another class contains implementation of separate methods to handle these input values accordingly. E.g. Method A can be called only if input value is A, method B can be called only if input value is B.

The good part is that all these methods accept exactly same parameter and its types. e.g. A, B, C, are all of same type.

Is there any way to handle this in Scala?

We can use ENUM and case logic but looking for a better way to do this? E. G. Scala reflection API or using case classes.


Solution

  • From your question, it's not at all obvious why you would want to use dynamic invocation or reflection.


    If all values A, ..., D belong to a common type Foo, then it probably won't get much easier than with case-classes:

    sealed trait Foo
    case class A(a1: Int, a2: String) extends Foo
    case class B(b: Double) extends Foo
    case class C(c: String) extends Foo
    case object D extends Foo
    

    Every time you want to transform A, ..., D to some other result type R, you can simply write a match-case, and handle the different cases there. If you use it so often that it is still too verbose, you can introduce an eliminator of the type Foo, which accepts bunch of functions, and hides the pattern matching inside:

    object Foo {
      def elim[R](
        aElim: (Int, String) => R,
        bElim: Double => R,
        cElim: String => R,
        dElim: R
      )(f: Foo): R = f match {
        case A(a1, a2) => aElim(a1, a2)
        case B(b) => bElim(b)
        case C(s) => cElim(s)
        case D => dElim
      }
    }
    

    In this way, you can trade the naming of the cases in match-case for a shorter syntax, e.g. you could construct an eliminator like this:

    val fooToString = Foo.elim(_ + _, "" + _, identity, "D") _    
    

    or apply it to a list of As ... Ds directly:

    List(A(42, "foo"), B(3.1415d), C("bar"), D).
      map(Foo.elim(_ + _, "" + _, identity, "D")).
      foreach(println)
    

    This would output

    42foo
    3.1415
    bar
    D
    

    If this isn't worth it, just use case classes with usual pattern matching.