2

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

  • _"But by having many functions, the pattern match is getting duplicated and hard to maintain"_. I personally would rather try to fix this, maybe create a `main`-like function that does everything so you only call one in the pattern match? Or maybe you can create a data structure that contains all the typeclasses you need and you would return that from the pattern match. – Luis Miguel Mejía Suárez Sep 24 '21 at 13:56
  • Thank you for your comment 1- Create one main-like function that I call from the pattern match is not really possible in my case, I have many main-functions/jobs for different use cases that need to use the same pattern matching 2- I though about returning all the type classes, that is not ideal neither because I have many entries in my pattern match and each time I add a new typeclass I will need to update the structure for all entries, while the instances already exist in the corresponding companion objects. – Wassim Maaoui Sep 24 '21 at 14:46
  • 2
    I would just keep the pattern match then, you need to do a mapping from runtime value into a compile-time type, and a pattern match is the easiest / most natural way to do that. If not all the cases share the same logic also seems like a good reason for just doing that match, not all duplication is bad and in this case, it seems the duplication for cases would just give you clarity and simplicity of change in the future. – Luis Miguel Mejía Suárez Sep 24 '21 at 15:00

1 Answers1

1

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

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66