Filtering implicits

trait Foo[A , B]

implicit val x = new Foo[Int, Boolean]

implicit val y = new Foo[Int, String]

def f[A](implicit foo: Foo[Int, A], ev: A =:= Boolean]) = foo
f

The code above doesn’t compile because the first implicit parameter is ambiguous, however, only one candidate satisfies both constraints. Is there a way to defer ambiguity checks for an implicit parameter until implicit search as a whole completes, and only then fail if more than one candidate was found (in other words, the search would have to try all branches instead of just one)?

If not, is there another way to implement this sort of “filtering” pattern on implicits.

There’s a related discussion on coherency, but the ask there is for the compiler to pick a candidate arbitrarily in cases where it’s known a priori that all candidates are equivalent, so not quite the same thing.

Edit: Dotty is going in the opposite direction, where ambiguity errors now terminate the search as a whole rather than just the branch they’re on.

Move the evidence first so A is resolved before searching for Foo.

trait Foo[A, B]

implicit val x: Foo[Int, Boolean] = new Foo[Int, Boolean] {}  
implicit val y: Foo[Int, String] = new Foo[Int, String] {}

def f[A](implicit ev: A =:= Boolean, foo: Foo[Int, A]): Foo[Int, A] = foo 

f // works.

Yes, that works for my (contrived) example because the compiler can infer that A must be Boolean from the evidence since the only available instance is:

implicit def refl[A]: A =:= A = singleton.asInstanceOf[A =:= A]

but it will break again if (for example) I change equality to conformance.

For context, what I’m ultimately trying to do is to summon types tagged with Shapeless HLists and filter by their tags. Unfortunately, that doesn’t work. The only alternative I can think of is to enumerate all possible lists of tags up front, which is cumbersome to do at the type-level, and might bloat compilation times too much to be acceptable.

Here’s a case where re-ordering doesn’t help:

import shapeless.ops.hlist.Selector
import shapeless.{::, HNil}

trait Tagged[A, L <: HList]

implicit val x = new Tagged[Int, Boolean :: String :: HNil] {}

implicit val y = new Tagged[Int, String :: HNil] {}

def f[A <: HList](implicit t: Tagged[Int, A], ev: Selector[A, Boolean]) = t

def g[A <: HList](implicit ev: Selector[A, Boolean], t: Tagged[Int, A]) = t

f //ambiguous
g //infers A = Boolean :: Nothing from ev and fails on ta