【问题标题】:Force dependent types resolution for implicit calls强制隐式调用的依赖类型解析
【发布时间】:2019-11-12 03:14:12
【问题描述】:

我有一个包装器特性:


trait Wrapper[T] {

  ...
  type Own[F[_]] <: OwnThing[F]
  def ask[F[_]](implicit own: Own[F])

}

和不同的实现,这里是一个例子:

class CombinedWrapper[A, B](wrapperA: Wrapper[A], wrapperB: Wrapper[B]) extends Wrapper[(A, B)] {
  override type Own[F[_]] = SomeOwnThing[(A, B), wrapperA.Own[F], wrapperB.Own[F]]
  override def ask[F[_]](implicit own: Own[F]) = ???
}

但是隐式解析不会解析依赖路径的类型,因此找不到任何隐式。

有没有办法强制解析依赖路径的类型? 至于现在,我只是将它们视为变量,但在类型级别。

编辑: 更完整的版本:


trait Wrapper[T] {

  ...
  type Own[F[_]] <: OwnThing[F, T]
  def ask[F[_]](implicit own: Own[F])

}

sealed trait OwnThing[F[_], A]

trait SomeOwnThing[F[_], A, B, ThingA <: OwnThing[F, A], ThingB <: OwnThing[F, B]] extends OwnThing[F, (A, B)] {
  def underlyingA: ThingA
  def underlyingB: ThingB
}

class CombinedWrapper[A, B](wrapperA: Wrapper[A], wrapperB: Wrapper[B]) extends Wrapper[(A, B)] {
  override type Own[F[_]] = SomeOwnThing[F, A, B, wrapperA.Own[F], wrapperB.Own[F]]
  override def ask[F[_]](implicit own: Own[F]) = ???
}

Edit2:一个无效的例子

trait SimpleOwn[F[_], A] extends OwnThing[F, A]

class SimpleWrapper[T] extends Wrapper[T] {
    override type Own[F[_]] = SimpleOwn[F, T]
    override def ask[F[_]](implicit own: Own[F]) = ???
  }

val combined = new CombinedWrapper[String, Int](new SimpleWrapper[String], new SimpleWrapper[Int])
type Id[A] = A

//Simple case for base types
implicit val intOwn: SimpleOwn[Id, Int] = new SimpleOwn[Id, Int]
implicit val stringOwn: SimpleOwn[Id, String] = new SimpleOwn[Id, String]

//Should combine the two above
implicit def composeOwnIds[A, B, ThingA <: OwnThing[Id, A], ThingB <: OwnThing[Id, B]](implicit aOwn: ThingA, bOwn: ThingB): SomeOwnThing[Id, A, B, ThingA, ThingB] = new SomeOwnThing {
  override def underlyingA: ThingA = aOwn
  override def underlyingB: ThingB = bOwn
}

//Should work but cant find implicit
combined.ask[Id]

Edit3:对我来说,问题的根源在于类型成员定义处的CombinedWrapper。我认为 Scala 没有解决定义中使用的路径相关类型。

我可以这么说是因为

new SimpleWrapper[String].ask

编译

【问题讨论】:

  • 关于您的示例代码的某些内容确实搞砸了。语法type A[F[_]] &lt;: B[F[_]] 甚至无效。此外wrapperA.Own[F] 没有意义。 wrapperA 是一个值,Own[F] 是一个类型。你想要的可能是Wrapper[A]#Own[F]
  • 是否有错误或只是复杂性?
  • 抱歉,按提交有点太早了。查看我更新的评论。
  • 我还想查看您遇到的问题的代码示例,即您说隐式解决方案应该起作用但不起作用的地方。
  • @MarkusAppel wrapperA.Own[F] 确实有意义:danielwestheide.com/blog/2013/02/13/…

标签: scala implicit type-level-computation path-dependent-type


【解决方案1】:

先修正几个错别字

//class CombinedWrapper[A, B](wrapperA: Wrapper[A], wrapperB: Wrapper[B]) extends Wrapper[(A, B)] {
  class CombinedWrapper[A, B](val wrapperA: Wrapper[A], val wrapperB: Wrapper[B]) extends Wrapper[(A, B)] { ...

//implicit val intOwn: SimpleOwn[Id, Int] = new SimpleOwn[Id, Int]
  implicit val intOwn: SimpleOwn[Id, Int] = new SimpleOwn[Id, Int] {}
//implicit val stringOwn: SimpleOwn[Id, String] = new SimpleOwn[Id, String]
  implicit val stringOwn: SimpleOwn[Id, String] = new SimpleOwn[Id, String] {}

//implicit def composeOwnIds[A, B, ThingA <: OwnThing[Id, A], ThingB <: OwnThing[Id, B]](implicit aOwn: ThingA, bOwn: ThingB): SomeOwnThing[Id, A, B, ThingA, ThingB] = new SomeOwnThing {
  implicit def composeOwnIds[A, B, ThingA <: OwnThing[Id, A], ThingB <: OwnThing[Id, B]](implicit aOwn: ThingA, bOwn: ThingB): SomeOwnThing[Id, A, B, ThingA, ThingB] = new SomeOwnThing[Id, A, B, ThingA, ThingB] { ...

那么调试隐式的标准方法是手动解决它们(可能显式指定一些类型参数)并查看编译错误。

new SimpleWrapper[String].ask
new SimpleWrapper[Int].ask

其实是

new SimpleWrapper[String].ask[Id](stringOwn)
new SimpleWrapper[Int].ask[Id](intOwn)

如果你尝试

combined.ask[Id](composeOwnIds(stringOwn, intOwn))

你会拥有

Error: type mismatch;
 found   : App.intOwn.type (with underlying type App.SimpleOwn[App.Id,Int])
 required: App.combined.wrapperB.Own[App.Id]
Error: type mismatch;
 found   : App.stringOwn.type (with underlying type App.SimpleOwn[App.Id,String])
 required: App.combined.wrapperA.Own[App.Id]

如果你尝试

combined.ask[Id](composeOwnIds[String, Int, SimpleOwn[Id, String], SimpleOwn[Id, Int]](stringOwn, intOwn))

你会拥有

Error: type mismatch;
 found   : App.SomeOwnThing[App.Id,String,Int,App.SimpleOwn[App.Id,String],App.SimpleOwn[App.Id,Int]]
 required: App.combined.Own[App.Id]
    (which expands to)  App.SomeOwnThing[App.Id,String,Int,App.combined.wrapperA.Own[App.Id],App.combined.wrapperB.Own[App.Id]]

如果你替换

val combined = new CombinedWrapper[String, Int](new SimpleWrapper[String], new SimpleWrapper[Int])

val strWrapper = new SimpleWrapper[String]
val intWrapper = new SimpleWrapper[Int]
val combined = new CombinedWrapper[String, Int](strWrapper, intWrapper)

然后

combined.ask[Id](composeOwnIds[String, Int, strWrapper.Own[Id], intWrapper.Own[Id]](stringOwn, intOwn))

会给

Error: type mismatch;
 found   : App.SomeOwnThing[App.Id,String,Int,App.strWrapper.Own[App.Id],App.intWrapper.Own[App.Id]]
    (which expands to)  App.SomeOwnThing[App.Id,String,Int,App.SimpleOwn[App.Id,String],App.SimpleOwn[App.Id,Int]]
 required: App.combined.Own[App.Id]
    (which expands to)  App.SomeOwnThing[App.Id,String,Int,App.combined.wrapperA.Own[App.Id],App.combined.wrapperB.Own[App.Id]]
  combined.ask[Id](composeOwnIds[String, Int, strWrapper.Own[Id], intWrapper.Own[Id]](stringOwn, intOwn))

问题是尽管combined.wrapperAstrWrappercombined.wrapperBintWrapper,但是combined.wrapperA.Own[Id]strWrapper.Own[Id] 类型不同,combined.wrapperB.Own[Id]intWrapper.Own[Id] 类型也不同。

例如,如果你有

trait MyTrait { type T }
val mt = new MyTrait {}
val mt1 = mt

那么值相等但类型不同

//  implicitly[mt.T =:= mt1.T] // doesn't compile
//  implicitly[mt1.T =:= mt.T] // doesn't compile

尝试修改CombinedWrapper添加更多类型参数,并在调用处指定具体依赖类型

  trait Wrapper[T] {
    type Own[F[_]] <: OwnThing[F, T]
    def ask[F[_]](implicit own: Own[F])
  }

  sealed trait OwnThing[F[_], A]

  trait SomeOwnThing[F[_], A, B, ThingA <: OwnThing[F, A], ThingB <: OwnThing[F, B]] extends OwnThing[F, (A, B)] {
    def underlyingA: ThingA
    def underlyingB: ThingB
  }

//  class CombinedWrapper[A, B](val wrapperA: Wrapper[A], val wrapperB: Wrapper[B]) extends Wrapper[(A, B)] {
//    override type Own[F[_]] = SomeOwnThing[F, A, B, wrapperA.Own[F], wrapperB.Own[F]]
//    override def ask[F[_]](implicit own: Own[F]) = ???
//  }

  class CombinedWrapper[A, B, OwnA[F[_]] <: OwnThing[F, A], OwnB[F[_]] <: OwnThing[F, B]](
    val wrapperA: Wrapper[A] { type Own[F[_]] = OwnA[F] }, 
    val wrapperB: Wrapper[B] { type Own[F[_]] = OwnB[F] }
  ) extends Wrapper[(A, B)] {
    override type Own[F[_]] = SomeOwnThing[F, A, B, OwnA[F], OwnB[F]]
    override def ask[F[_]](implicit own: Own[F]) = ???
  }

  trait SimpleOwn[F[_], A] extends OwnThing[F, A]

  class SimpleWrapper[T] extends Wrapper[T] {
    override type Own[F[_]] = SimpleOwn[F, T]
    override def ask[F[_]](implicit own: Own[F]) = ???
  }

//  val combined = new CombinedWrapper[String, Int](new SimpleWrapper[String], new SimpleWrapper[Int])
  val strWrapper = new SimpleWrapper[String]
  val intWrapper = new SimpleWrapper[Int]
  val combined = new CombinedWrapper[String, Int, strWrapper.Own, intWrapper.Own](strWrapper, intWrapper)

  type Id[A] = A

  implicit val intOwn: SimpleOwn[Id, Int] = new SimpleOwn[Id, Int] {}
  implicit val stringOwn: SimpleOwn[Id, String] = new SimpleOwn[Id, String] {}

  implicit def composeOwnIds[A, B, ThingA <: OwnThing[Id, A], ThingB <: OwnThing[Id, B]](implicit aOwn: ThingA, bOwn: ThingB): SomeOwnThing[Id, A, B, ThingA, ThingB] = new SomeOwnThing[Id, A, B, ThingA, ThingB] {
    override def underlyingA: ThingA = aOwn
    override def underlyingB: ThingB = bOwn
  }

  combined.ask[Id] // compiles

  new SimpleWrapper[String].ask  // compiles

使用Aux 类型

  trait Wrapper[T] {
    type Own[F[_]] <: OwnThing[F, T]
    def ask[F[_]](implicit own: Own[F])
  }

  object Wrapper {
    type Aux[T, Own0[F[_]] <: OwnThing[F, T]] = Wrapper[T] { type Own[F[_]] = Own0[F] }
  }

  class CombinedWrapper[A, B, OwnA[F[_]] <: OwnThing[F, A], OwnB[F[_]] <: OwnThing[F, B]](
    val wrapperA: Wrapper.Aux[A, OwnA], val wrapperB: Wrapper.Aux[B, OwnB]
  ) extends Wrapper[(A, B)] {
    override type Own[F[_]] = SomeOwnThing[F, A, B, OwnA[F], OwnB[F]]
    override def ask[F[_]](implicit own: Own[F]) = ???
  }

【讨论】:

  • 感谢您的回答。我已将类型成员作为类型参数移动。这很烦人,因为人类无法读取结果类型,但它确实有效。我对您的 MyTrait 示例非常感兴趣。你能解释一下为什么这两种类型不同吗?
  • @Merlin 关于类型参数与类型成员,这就是使用Aux 模式的原因(见更新)gigiigig.github.io/posts/2015/09/13/aux-pattern.htmlvlachjosef.com/aux-pattern-evolutionstackoverflow.com/questions/43900674/…
  • @Merlin 关于MyTrait,这是创建path-dependent typesclass A { class B}val a1: A = new Aval a2: A = new Aval a2: A = new Aval b1: a1.B = new a1.Bval b2: a2.B = new a2.B的值的标准方法取决于两个值是否相等取决于equals 的实现,但不同前缀的类型总是不同的。
  • @Merlin 好吧,对于mtmt1,甚至引用都是相等的。但无论如何,类型是不同的。 mt1 可以是var,在val mt = new MyTrait {} var mt1 = mt 之后我们可以更改mt1 = new MyTrait {}。首先引用是相等的,现在它们不同了,但这不是更改 var 类型的原因 :) 所以类型从一开始就不同。
猜你喜欢
  • 2014-11-17
  • 1970-01-01
  • 2012-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-17
  • 2019-12-06
相关资源
最近更新 更多