Why is this not an ambiguous implicit?

Can someone please point me at or explain the rule that makes this not ambiguous (in Scala 3.1)?

We have two typeclass instances in scope, one a subtype of the other, both satisfy the constraint, and yet there is no ambiguity:

trait Typeclass[T]

trait Typeclass2[T] extends Typeclass[T]


object Context:
  given Typeclass[Int] = new Typeclass2[Int]{}
  given Typeclass2[Int] = new Typeclass2[Int]{}

import Context.{*, given}

def foo(using Typeclass[Int]) = "OK"

foo

Scastie

This is because the compiler will choose the most “specific” instance and only fault with an ambiguous error when there is more than one instance that qualifies as more “specific”.
However, to be honest, the definition of “specific” here is surprising really.

I have an example here: https://github.com/BalmungSan/scala-functional-programming-tutorial/blob/7f33aeb907c85e0dc52f6a1fa80b46d73af2d598/scala-intro/src/main/scala/co/edu/eafit/dis/progfun/scala/intro/ImplicitsNotes.scala#L16-L81

Hope that helps :slight_smile:

1 Like

I think this is supposed to be the “easy” case, so I’d be interested to hear why it is surprising.

The new spec is similar to the old one.

If A <: B then A is “as specific as” B, so A scores a “point”.

The converse isn’t true, so A wins.

The other way to score a point depends on where the definition appears. You get a point for being further down the class hierarchy.

In the example, Typeclass scores a point by the second rule if defined as follows. Since the score is tied, the givens are ambiguous when defined this way. This might be surprising because “where the given is defined” is not reflected in the required type.

class X:
  given Typeclass2[Int] = new Typeclass2[Int]{}

object Context extends X:
  given Typeclass[Int] = new Typeclass2[Int]{}
1 Like