5
votes

Given some method

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

Does the order of a before b within the implicit parameter list matter for type inference?

I thought only the placement of parameters within different parameter lists matters, e.g. type information flows only through parameter lists from left to right.

I'm asking because I noticed that changing the order of implicit parameters within the singly implicit list made a program of mine compile.

Real example

The following code is using:

  • shapeless 2.1.0
  • Scala 2.11.5

Here is a simple sbt build file to help along with compiling the examples:

scalaVersion := "2.11.5"

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

scalaSource in Compile := baseDirectory.value

Onto the example. This code compiles:

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

and this code fails

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

with the following compile error

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)
                        ^
3
The order should not matter, at least in isolation. However changing the order of the implicit definitely changes the method's signature, so it changes the type of the class/trait it is defined in. In any wase, you should definitely post a code snippet demonstrating how changing the order changes the compilation result, otherwise it's hard to discuss about the issue.Régis Jean-Gilles
@RégisJean-Gilles I've added a somewhat small example.ziggystar

3 Answers

2
votes
  1. Normally it should not matter. If you look at the language spec it makes no mention about resolution being dependent on parameter order.

  2. I looked at the source code of shapeless, and I could not come up with any reason why this error would present itself.

  3. And doing a quick search through the bug repo of the language I found a similar issue that was apparently resolved. But it does not state if the fix involved treating the symptom (making context bounds not break compilation) or the cause (restrictions on implicit parameter ordering.)

Therefore I would argue that this is a compiler bug, and it is tightly related to the issue linked in point 3.

Also, I would suggest you submit a bug report if you can find a second opinion that resulted from a more rigorous analysis than my own :)

Hope this puts your mind at rest. Cheers!

1
votes

According to my reading of the comments of the issue mentioned by Lorand Szakacs, I come to the conclusion that the order of implicit parameters matters in the current version 2.11 of the Scala compiler.

This is because the developers participating in the discussion appear to assume that the order matters; they do not state it explicitly.

I'm not aware of the language spec mentioning anything about this topic.

0
votes

Reordering them will only break code that explicitly passes them, as well as all compiled code. Everything else will be unaffected.