The following does not compile because of the last line:
object ImplicitWrappedTraitWithType {
trait Wrapper[+T] {
def unwrap: T
}
object Wrapper {
def apply[T](implicit w: Wrapper[T]): Wrapper[T] = w
}
trait IO[In] {
type Out
def out: Out
}
implicit def decoder[In]: Wrapper[IO[In] {type Out = String}] = new Wrapper[IO[In] {type Out = String}] {
override def unwrap: IO[In] {type Out = String} = new IO[In] {
override type Out = String
override val out: Out = "yeah"
}
}
val wrap = Wrapper[IO[String]]
val io: IO[String] = wrap.unwrap
val out: String = io.out //actual type: unwrap.Out
}
What can I do to convince the compiler that val out
is a String
?
Pre-edit - ignore this
Example 1 - this does not compile:
object ImplicitWrappedTraitWithType {
class Wrapper[T]
object Wrapper {
def apply[T](implicit w: Wrapper[T]): Wrapper[T] = w
}
trait IO[In] {
type Out
}
implicit def decoder[In]: Wrapper[IO[In] {type Out = String}] = null
//client code
Wrapper[IO[String]]
}
Example 2 - whereas this does:
object ImplicitWrappedTraitWithType {
class Wrapper[T]
object Wrapper {
def apply[T](implicit w: Wrapper[T]): Wrapper[T] = w
}
trait IO[In] {
type Out
}
implicit def decoder[In]: Wrapper[IO[In]] = null
//client code
Wrapper[IO[String]]
}
In the client code I don't know what the type of Out
will be, but I need to be able to access it when I extract an instance of IO
from Wrapper
(code for that not shown).
How must 'Example 1' be changed for this to compile, while retaining the Out parameter in a way that is visible for the client code.
(Please comment if this formulation is unclear)
You only need two minor edits to your code.
trait Wrapper[+T] {
def unwrap: T
}
object Wrapper {
def apply[T](implicit w: Wrapper[T]): w.type = w
}
trait IO[In] {
type Out
def out: Out
}
implicit def decoder[In]: Wrapper[IO[In] {type Out = String}] = new Wrapper[IO[In] {type Out = String}] {
override def unwrap: IO[In] {type Out = String} = new IO[In] {
override type Out = String
override val out: Out = "yeah"
}
}
val wrap = Wrapper[IO[String]]
val io = wrap.unwrap
val out: String = io.out
The most important is to change the return type of the apply
method to w.type
. That way the full type of w
(including all refinements) is retained. If you write Wrapper[T]
as return type and you ask for a Wrapper[T]
for T
equal to IO[String]
, you will get a Wrapper[IO[String]]
and all extra knowledge like {type Out = String}
will be lost.
Second: in val io: IO[String] = wrap.unwrap
you say that io
is an IO[String]
. Again, all extra knowledge is lost. So just let the compiler infer the type of io
.
Another thing: if you don't want Wrapper
to be covariant in T
, you can just leave off the variance annotation and change your apply
method.
trait Wrapper[T] {
def unwrap: T
}
object Wrapper {
def apply[T](implicit w: Wrapper[_ <: T]): w.type = w
}
That way the compiler still knows it has to look for a subtype of IO[String]
if you call Wrapper.apply[IO[String]]
. Because IO[String]{type out = String}
is a subtype of IO[String]
, they are not equal.
Not clear what you are trying to achieve, but does that solve your problem? (note the -T in Wrapper)
object ImplicitWrappedTraitWithType {
class Wrapper[-T]
object Wrapper {
def apply[T](implicit w: Wrapper[T]): Wrapper[T] = w
}
trait IO[In] {
type Out
}
implicit def decoder[In]: Wrapper[IO[In]] = null
}
import ImplicitWrappedTraitWithType._
trait IOString[In] extends IO[In] {
type Out = String
}
//client code
Wrapper[IOString[Int]]
I won't put any restriction on the Out
type when trying to find an implicit decoder for it.
Whether the user defines it's own decoder and in that case you don't care about In
and Out
, or you have some basic decoders and you provide it, eg: IO[Int, String], IO[Int, Boolean], etc.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.