I was expecting as Input
is a sealed trait and I made the implicits implementation for both InputA
and InputB
that during the runtime it select by itself accordingly to the type provided.
Implicits (instances of a type class) are resolved at compile time (not runtime), during type checking (compilation phase typer
).
Runtime vs. Compile time
In val g: Input = InputB("1")
it becomes known that g
is an InputB
and not InputA
only at runtime. At compile time it's only known that g
is an Input
.
So either let compiler know at compile time that g
is InputB
val g: InputB = InputB("1")
or define an instance of the type class for Input
implicit val processInput: Process[Input] = {
case u: InputA => implicitly[Process[InputA]].give(u)
case u: InputB => implicitly[Process[InputB]].give(u)
}
val g: Input = InputB("1")
val res3: Input = UseProcess.run(g) // InputB(1add)
As you can see, if your logic is based on a runtime value, you need pattern matching (occurring mostly at runtime) rather than implicit (type class) resolution. Implicits (type classes) are kind of "pattern matching" at compile time.
You can also derive an instance Process[Input]
based on instances Process[InputA]
, Process[InputB]
rather than define it manually for every sealed-trait hierarchy.
// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.{:+:, CNil, Coproduct, Inl, Inr, Generic}
implicit def genericProcess[A, C <: Coproduct](implicit
generic: Generic.Aux[A, C],
process: Process[C]
): Process[A] = a => generic.from(process.give(generic.to(a)))
implicit def cNilProcess: Process[CNil] = _.impossible
implicit def cConsProcess[H, T <: Coproduct](implicit
hProcess: Process[H],
tProcess: Process[T]
): Process[H :+: T] =
_.eliminate(h => Inl(hProcess.give(h)), t => Inr(tProcess.give(t)))
val g: Input = InputB("1")
val res3: Input = UseProcess.run(g) // InputB(1add)
Use the lowest subtype in a typeclass?
Type class instance for case objects defined in sealed trait
How to accept only a specific subtype of existential type?
Covariant case class mapping to its base class without a type parameter and back
An alternative to defining an instance Process[Input]
at compile time could be to run compilation and resolve instances at runtime. Surely, this approach is less type-safe. If there is no instance TC[I]
the following code fails at runtime.
// libraryDependencies += scalaOrganization.value % "scala-reflect" % scalaVersion.value
import scala.reflect.runtime.{currentMirror => rm}
import scala.reflect.runtime.universe._
// libraryDependencies += scalaOrganization.value % "scala-compiler" % scalaVersion.value
import scala.tools.reflect.ToolBox
val tb = rm.mkToolBox()
def getInstance[TC[_]] = new PartiallyAppliedGetInstance[TC]
class PartiallyAppliedGetInstance[TC[_]] {
def apply[I](i: I)(implicit wtt: WeakTypeTag[TC[_]]): TC[I] =
tb.eval(
tb.untypecheck(
tb.inferImplicitValue(
appliedType(
weakTypeOf[TC[_]].typeConstructor,
rm.classSymbol(i.getClass).toType
)
)
)
).asInstanceOf[TC[I]]
}
val g: Input = InputB("1")
val res3: Input = UseProcess.run(g)(getInstance[Process](g)) // InputB(1add)
Scala upper type bounds for reflectioned type
Is there anyway, in Scala, to get the Singleton type of something from the more general type?
Load Dataset from Dynamically generated Case Class