【问题标题】:Implicitly wrapped trait with type member does not compile具有类型成员的隐式包装特征无法编译
【发布时间】:2016-11-01 09:06:08
【问题描述】:

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

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 的类型是什么,但是当我从Wrapper 提取IO 的实例时我需要能够访问它(未显示的代码)。

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

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

【问题讨论】:

  • 在客户端代码中我不知道 Out 的类型是什么,但是当我从 Wrapper 提取 IO 实例时我需要能够访问它但您可以通过IO[T].Out 看到它。我不确定我是否理解这个问题。
  • @YuvalItzchakov 在第二个示例中 Out 不受约束。
  • @Jasper-M 我看到它是不受约束的,但我不确定 OP 是否希望它成为他所说的“可见性”。我试图理解他的意思。
  • 更新问题
  • 辅助模式在这里可能会有所帮助:gigiigig.github.io/posts/2015/09/13/aux-pattern.html

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


【解决方案1】:

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

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] 写为返回类型,并要求T 等于IO[String]Wrapper[T],您将获得Wrapper[IO[String]],并且所有额外的知识(如{type Out = String})都将丢失。

第二:在val io: IO[String] = wrap.unwrap 中,你说ioIO[String]。同样,所有额外的知识都丢失了。所以只要让编译器推断io的类型。

另一件事:如果您不希望 WrapperT 中是协变的,您可以省略方差注释并更改您的 apply 方法。

trait Wrapper[T] {
  def unwrap: T
}

object Wrapper {
  def apply[T](implicit w: Wrapper[_ <: T]): w.type = w
}

这样编译器仍然知道如果您调用Wrapper.apply[IO[String]],它必须寻找IO[String]子类型。因为IO[String]{type out = String}IO[String] 的子类型,所以它们不相等。

【讨论】:

  • 正确。谢谢:)
【解决方案2】:

不清楚您要达到什么目标,但这能解决您的问题吗? (注意 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] 等。

【讨论】:

  • -T 没有做任何关于编译的事情。然而,这让我也尝试了 +T 哪个“有效” - 有点。尽管我开始怀疑我的问题没有解决方案,但我正在稍微更新一下问题。
  • 也许您同时更改了代码。您在此处发布的示例我稍作重构不适用于 T 或 +T,仅适用于 -T。不管怎样,我看到你走了另一条路,发现我的方法更好。玩得开心! ;-)
猜你喜欢
  • 2015-02-14
  • 2021-01-24
  • 1970-01-01
  • 2015-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多