Scala resolving Class/Type at runtime + type class constraint

Viewed 192

I have a generic function that require a HasMoveCapability implicit instance of the type T (type class pattern)

  trait HasMoveCapability[T]

  def doLogic[T: TypeTag: HasMoveCapability](): Unit = println(typeTag[T].tpe)

Then I have these two classes which have implicit instances for HasMoveCapability[T]

  case class Bird()
  object Bird {
    implicit val hasMoveCapability = new HasMoveCapability[Bird]{}
  }
  case class Lion()
  object Lion {
    implicit val hasMoveCapability = new HasMoveCapability[Lion]{}
  }

My question is the following: I need to resolve the type (Lion or Bird) at runtime depending on an argument and call the function doLogic with the good type.

I tried

  val input: String = "bird" // known at runtime
  val resolvedType: TypeTag[_] = input match {
    case "bird" => typeTag[Bird]
    case "lion" => typeTag[Lion]
  }

  doLogic()(resolvedType) // doesn't compile 
// `Unspecified value parameters: hasMoveCapability$T$1: HasMoveCapability[NotInferredT]`

What I would like to do is something like:

  val resolvedType: TypeTag[_: HasMoveCapability] = input match{...}

The workaround that I am using so far is to call the function in the pattern match:

input match {
    case "bird" => doLogic[Bird]
    case "lion" => doLogic[Lion]
  }

But by having many functions, the pattern match is getting duplicated and hard to maintain.

I am open to change the design if you have any suggestions :D

1 Answers

You should describe your problem better. Currently your type class HasMoveCapability doesn't seem to do anything useful. Currently what you do seems a hard way to transform the string "bird" into "Bird", "lion" into "Lion".

If you control the code of doLogic you seem not to need TypeTag. TypeTag / ClassTag is a way to persist information from compile time to runtime. You seem to do something in reverse direction.

Type classes / implicits are resolved at compile time. You can't resolve something at compile time based on runtime information (there is no time machine taking you from the future i.e. runtime to the past i.e. compile time). Most probably you need ordinary pattern matching rather than type classes (TypeTag, HasMoveCapability).

In principle you can run compiler at runtime, then you'll have new compile time inside runtime, and you'll be able to infer types, resolve implicits etc.

import scala.tools.reflect.ToolBox
import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe.{TypeTag, typeTag}

object App {    
  trait HasMoveCapability[T]

  def doLogic[T: TypeTag: HasMoveCapability](): Unit = println(typeTag[T].tpe)

  case class Bird()
  object Bird {
    implicit val hasMoveCapability = new HasMoveCapability[Bird]{}
  }
  case class Lion()
  object Lion {
    implicit val hasMoveCapability = new HasMoveCapability[Lion]{}
  }

  val input: String = "bird" // known at runtime

  val tb = currentMirror.mkToolBox()
  tb.eval(tb.parse(s"import App._; doLogic[${input.capitalize}]")) //App.Bird

  def main(args: Array[String]): Unit = ()
}

scala get generic type by class

Related