Scala 中隐式参数的顺序重要吗?

Does the order of implicit parameters matter in Scala?

给出一些方法

def f[A,B](p: A)(implicit a: X[A,B], b: Y[B])

隐式参数列表中 b 之前 a 的顺序对类型推断有影响吗?

我认为只有参数在不同参数列表中的位置很重要,例如类型信息仅从左到右流过参数列表。

我问这个问题是因为我注意到更改单隐式列表中隐式参数的顺序使我的程序可以编译。

实例

正在使用以下代码:

这是一个简单的 sbt 构建文件,可帮助编译示例:

scalaVersion := "2.11.5"

libraryDependencies += "com.chuusai" %% "shapeless" % "2.1.0"

scalaSource in Compile := baseDirectory.value

举个例子。此代码编译:

import shapeless._
import shapeless.ops.hlist.Comapped

class Foo {
  trait NN
  trait Node[X] extends NN
  object Computation {
    def foo[LN <: HList, N <: HList, TupN <: Product, FunDT]
    (dependencies: TupN)
    (computation: FunDT)
    (implicit tupToHlist: Generic.Aux[TupN, LN], unwrap: Comapped.Aux[LN, Node, N]) = ???
//    (implicit unwrap: Comapped.Aux[LN, Node, N], tupToHlist: Generic.Aux[TupN, LN]) = ???

    val ni: Node[Int] = ???
    val ns: Node[String] = ???
    val x = foo((ni,ns))((i: Int, s: String) => s + i.toString)
  }
}

此代码失败

import shapeless._
import shapeless.ops.hlist.Comapped

class Foo {
  trait NN
  trait Node[X] extends NN
  object Computation {
    def foo[LN <: HList, N <: HList, TupN <: Product, FunDT]
    (dependencies: TupN)
    (computation: FunDT)
//    (implicit tupToHlist: Generic.Aux[TupN, LN], unwrap: Comapped.Aux[LN, Node, N]) = ???
    (implicit unwrap: Comapped.Aux[LN, Node, N], tupToHlist: Generic.Aux[TupN, LN]) = ???

    val ni: Node[Int] = ???
    val ns: Node[String] = ???
    val x = foo((ni,ns))((i: Int, s: String) => s + i.toString)
  }
}

有以下编译错误

Error:(22, 25) ambiguous implicit values:
 both method hnilComapped in object Comapped of type [F[_]]=> shapeless.ops.hlist.Comapped.Aux[shapeless.HNil,F,shapeless.HNil]
 and method hlistComapped in object Comapped of type [H, T <: shapeless.HList, F[_]](implicit mt: shapeless.ops.hlist.Comapped[T,F])shapeless.ops.hlist.Comapped.Aux[shapeless.::[F[H],T],F,shapeless.::[H,mt.Out]]
 match expected type shapeless.ops.hlist.Comapped.Aux[LN,Foo.this.Node,N]
    val x = foo((ni,ns))((i: Int, s: String) => s + i.toString)
                        ^
Error:(22, 25) could not find implicit value for parameter unwrap: shapeless.ops.hlist.Comapped.Aux[LN,Foo.this.Node,N]
    val x = foo((ni,ns))((i: Int, s: String) => s + i.toString)
                        ^
Error:(22, 25) not enough arguments for method foo: (implicit unwrap: shapeless.ops.hlist.Comapped.Aux[LN,Foo.this.Node,N], implicit tupToHlist: shapeless.Generic.Aux[(Foo.this.Node[Int], Foo.this.Node[String]),LN])Nothing.
Unspecified value parameters unwrap, tupToHlist.
    val x = foo((ni,ns))((i: Int, s: String) => s + i.toString)
                        ^

重新排序它们只会破坏明确传递它们的代码,以及所有已编译的代码。其他一切都不会受到影响。

  1. 正常情况应该没有关系。如果您查看 language spec,它没有提及分辨率取决于参数顺序。

  2. 我查看了 shapeless 的源代码,但我想不出任何会出现此错误的原因。

  3. 并通过语言的错误回购进行快速搜索,我发现 similar issue 显然已解决。但它没有说明修复是否涉及处理症状(使上下文边界不破坏编译)或原因(对隐式参数排序的限制。)

因此我认为这是一个编译器错误,它与第 3 点中链接的问题密切相关。

此外,如果您能找到比我自己的分析更严格的第二个意见,我建议您提交错误报告 :)

希望这能让您高枕无忧。干杯!

根据我对 Lorand Szakacs 提到的 the issue 的评论的阅读,我得出的结论是隐式参数的顺序在当前版本 2.11 的 Scala 编译器中很重要。

这是因为参与讨论的开发人员似乎认为顺序很重要;他们没有明确说明。

我不知道语言规范提到了关于这个主题的任何内容。