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].