具有类型成员的隐式包装特征无法编译

时间:2016-11-01 09:06:08

标签: scala generics type-parameter type-alias type-members

由于最后一行,以下内容无法编译:

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
}

如何说服编译器val outString

预编辑 - 忽略此

示例1 - 这不会编译:

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

示例2 - 而这样做:

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

在客户端代码中,我不知道Out的类型是什么,但是当我从IO中提取Wrapper的实例时,我需要能够访问它(未显示的代码)。

如何更改“示例1”以进行编译,同时以对客户端代码可见的方式保留Out参数。

(如果这个表述不清楚,请评论)

2 个答案:

答案 0 :(得分:3)

您只需对代码进行两次小修改。

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

最重要的是将apply方法的返回类型更改为w.type。这样就保留了完整类型的w(包括所有改进)。如果您将Wrapper[T]写为返回类型,并且要求Wrapper[T] T等于IO[String],您将获得Wrapper[IO[String]]以及所有额外知识,例如{ {1}}将丢失。

第二:在{type Out = String}中,您说val io: IO[String] = wrap.unwrapio。同样,所有额外的知识都会丢失。所以让编译器推断出IO[String]的类型。

另一件事:如果您不希望ioWrapper中变为协变,则可以省略方差注释并更改T方法。

apply

如果你调用trait Wrapper[T] { def unwrap: T } object Wrapper { def apply[T](implicit w: Wrapper[_ <: T]): w.type = w } ,那么编译器仍然知道它必须寻找 IO[String]的子类型。由于Wrapper.apply[IO[String]]IO[String]{type out = String}的子类型,因此它们不相等。

答案 1 :(得分:1)

不清楚你想要实现的目标,但这能解决你的问题吗? (注意Wrapper中的-T)

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

在尝试为其找到隐式解码器时,我不会对Out类型施加任何限制。

用户是否定义了自己的解码器,在这种情况下,您不关心InOut,或者您有一些基本的解码器并且您提供它,例如:IO [Int,String],IO [Int,Boolean]等