You can just do
val nc: notAContainer = notAContainer(status.pending, "abc")
val nc1: notAContainer = notAContainer(status.ready, "def")
container(nc.status, nc.commonValue)
container(nc1.status, nc1.commonValue)
You will have values of type container[status]
(not of its subtypes container[status.pending]
, container[status.ready]
).
Just in case, if it doesn't suit your use case please explain why (why you need values of types container[status.pending]
, container[status.ready]
, how you're going to use them etc.).
If this is really important (for example if the constructor of class container
behaves differently for different S
) then for example you can specify the type parameter and downcast
container[status.pending](nc.status.asInstanceOf[status.pending], nc.commonValue)
container[status.ready](nc1.status.asInstanceOf[status.ready], nc1.commonValue)
Or you can use pattern matching
nc.status match {
case s: status.ready => container[status.ready](s, nc.commonValue)
case s: status.pending => container[status.pending](s, nc.commonValue)
}
But the result will have type container[status]
.
You can even automate the pattern matching with a macro
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def matchStatus(nc: notAContainer): container[status] = macro matchStatusImpl
def matchStatusImpl(c: blackbox.Context)(nc: c.Tree): c.Tree = {
import c.universe._
val s = TermName(c.freshName("s"))
val cases = typeOf[status].typeSymbol.asClass.knownDirectSubclasses.map(symb => {
val typ = symb.asType.toType
val pattern = pq"$s: $typ"
cq"$pattern => container.apply[$typ]($s, $nc.commonValue)"
})
q"""
$nc.status match {
case ..$cases
}
"""
}
matchStatus(nc)
//scalac: App.this.nc.status match {
// case (s$macro$1 @ (_: App1.status.pending.type)) => App1.container.apply[App1.status.pending.type](s$macro$1, App.this.nc.commonValue)
// case (s$macro$1 @ (_: App1.status.ready.type)) => App1.container.apply[App1.status.ready.type](s$macro$1, App.this.nc.commonValue)
//}
Pattern matching (manual or with the macro) occurs at runtime. So at compile time we can't have a value of types container[status.pending]
, container[status.ready]
, only a value of type container[status]
.
If you really need a value of type container[status.pending]
or container[status.ready]
then you can use reflective compilation at runtime
import scala.reflect.runtime.{currentMirror => cm}
import scala.reflect.runtime.universe.Quasiquote
import scala.tools.reflect.ToolBox
object App {
val tb = cm.mkToolBox()
sealed trait status extends Product with Serializable
object status{
case object pending extends status
case object ready extends status
type ready = ready.type
type pending = pending.type
}
case class container[+S <: status](status : S, commonValue: String)
case class notAContainer(status : status, commonValue:String)
val nc: notAContainer = notAContainer(status.pending, "abc")
def main(args: Array[String]): Unit = {
// tb.eval(tb.parse(
// s"""import App._
// |val c = container[status.${nc.status}](status.${nc.status}, "${nc.commonValue}")
// |println(c)
// |""".stripMargin))
// val clazz = nc.status.getClass
// val classSymbol = cm.classSymbol(clazz)
val classSymbol = cm.reflect(nc.status).symbol
// val moduleSymbol = cm.moduleSymbol(clazz)
val moduleSymbol = classSymbol.owner.info.decl(classSymbol.name.toTermName) // (*)
tb.eval(q"""
import App._
val c = container[${classSymbol.toType}]($moduleSymbol, ${nc.commonValue})
println(c)
""")
}
}
(*) 1 2
Inside quasiquotes q"..."
the variable c
has type container[status.pending]
.