scala - 为什么在某些情况下会忽略高阶隐式?

标签 scala implicit

我收到了一个关于隐式的奇怪编译器错误,该错误实际上存在但由于某种原因无法找到。所以我构建了一个重现神秘行为的小测试用例。

trait Hide {
  type T
}
object HideString extends Hide {
  override type T = String
}
object HideBool extends Hide {
  override type T = Boolean
}

简单类型用作隐式转换的明确目标。
def id[H <: Hide, C](x : C)(implicit ev : C => H#T) : H#T = ev(x)
def drop[H <: Hide, C](x : C)(implicit ev : C => H#T) : Int = {
  println(ev(x))
  1
}
def idSeq[H <: Hide, C](x : Seq[C])(implicit ev : Seq[C] => Seq[H#T]) : Seq[H#T] = ev(x)
def dropSeq[H <: Hide, C](x : Seq[C])(implicit ev : Seq[C] => Seq[H#T]) : Int = {
  println(ev(x))
  1
}

依赖隐式转换的方法。它基本上是 2x2 矩阵。 id方法返回转换后的类型和 drop方法在内部使用转换并返回一些常量。普通方法对精确的隐式转换类型和 Seq 进行操作方法对序列进行操作。
implicit def exString(x : String) : HideString.type#T = x
implicit def highString[F[_]](x : F[String]) : F[HideString.type#T] = x

以上隐式转换 highString用高阶类型定义。
val s1 = id("sdf")
val s2 = drop("aero")

val r1 = idSeq(Seq("a", "bc"))
val r2 = dropSeq(Seq("i", "IO"))

尝试实际使用转换给我带来了一个错误:
ImplicitResolution.scala:98: error: No implicit view available from Seq[String] => Seq[test.implicits.HighReduction.Hide#T].
  val r2 = dropSeq(Seq("i", "IO"))

这可以总结为以下矩阵:
|        | id   | drop |
|--------+------+------|
| normal | pass | pass |
| seq    | pass | fail |

如果我对 dropSeq 使用精确定义的隐式转换通常找到的方法:
implicit def seqBool(x : Seq[Boolean]) : Seq[HideBool.type#T] = x

val a1 = idSeq(Seq(true, false))
val a2 = dropSeq(Seq(false, true))

此外,如果我明确指定隐式参数 dropSeq开始工作:
val r2i = dropSeq(Seq("i", "IO"))(highString[Seq] _)

这是最奇怪的事情。 highString隐式符合所有要求。并声明为 implicit ,所以它应该被编译器找到。如果是 idSeq它实际上被发现了。那么,为什么它在 dropSeq 中被忽略了?案件?

最佳答案

在您的情况下,idSeq 之间的唯一区别和 dropSeq是返回类型:您在 Scala 编译器中遇到了一些极端情况,值得向 Scala 社区发出信号。

也就是说,您的 idSeq 签名错误:H#X并不意味着指定 H 的 X 类型类型,而是 X对于 H 的任何实例(不是编译器已解决的实例,请参阅此处的 Daniel Sobral 解释 What does the `#` operator mean in Scala?)

您可能想要做的是在 H 和您的结果类型之间建立关系,如果您引入类型别名以获得更易读的签名,这会更容易:

object Hide {
  type HideAux[X] = Hide { type T = X}
}

然后,您可以像这样重新编写代码:
  def idSeq[B,H <: HideAux[B], C](x : Seq[C])(implicit ev : Seq[C] => Seq[B]) : Seq[B] = ev(x)
  def dropSeq[B,H <: HideAux[B], C](x : Seq[C])(implicit ev : Seq[C] => Seq[B]) : Int = {
    println(ev(x))
    1
  }

这段代码可以编译,还要注意如果你正确使用泛型和类型类,你就不需要两种不同的方法 ididSeq因为动态行为将由 typeclass 本身提供。

关于scala - 为什么在某些情况下会忽略高阶隐式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35664251/

相关文章:

scala - Spark - CSV - Nullable false 不抛出异常

scala - 是否可以为 Spark ML 中的随机森林创建通用训练管道?

scala - Scala 如何在这里使用我的所有内核?

java - 如何更新 Scala 中日期格式的列

scala - Slick:autoInc 在 MultiDBCakeExample 示例中是如何工作的?

德尔福(-XE): casting to a record type with implicit conversion

html - 使用隐式宽度和无空格换行防止元素拉伸(stretch)

c++ - 赋值运算符返回

在隐式类中创建的 Scala 不兼容的嵌套类型

scala - 明确的隐含