Search code examples
scalatypeclassimplicit

General way of ensuring implicit definition always has higher/lower priority


I have a somewhat complex typeclass situation in the following format:

sealed trait TypeClass[S <: MyType] {
  type Out <: MyType
}

sealed trait LowPriorityTypeClass {
  // Case: OtherTypeClass is NOT defined for the input type S.
  // The output type is the same as the input type.
  implicit def default[S <: MyType]: TypeClass.Aux[S, S] = ???
}

object TypeClass extends LowPriorityTypeClass {
  type Aux[S <: MyType, O <: MyType] = TypeClass[S] { type Out = O }

  // Case: OtherTypeClass is defined for the input type S.
  // The output type is the same as in the OtherTypeClass definition.
  implicit def hasOtherTC[S <: MyType, O <: MyType](
    implicit otherTC: OtherTypeClass.Aux[S, O],
  ): TypeClass.Aux[S, O] = ???
}

The default definition was put in the LowPriorityTypeClass trait with the intention of having a lower priority. However, an ambiguity with hasOtherTC still happens for some type S, apparently because the declaration of default is more specific than the declaration of hasOtherTC for that type S.

Is there a general way to ensure that an implicit definition will always have a higher/lower priority than other definition? (My question is not for the specific code above.)

Let me know if posting a more complete sample code would help.


Solution

  • Please see Why is this implicit ambiguity behaviour happening? including comments.

    There is no sense in introducing trait LowPriorityTypeClass in this case because anyway implicit default is more specific than hasOtherTC.

    There is no general way. You can use type classes Not (shapeless.Refute, implicitbox.Not) or shapeless.LowPriority, implicitbox.Priority or library https://github.com/milessabin/export-hook.

    object TypeClass {
      type Aux[S <: MyType, O <: MyType] = TypeClass[S] {type Out = O}
    
      implicit def hasOtherTC[S <: MyType, O <: MyType](implicit
                                                        otherTC: OtherTypeClass.Aux[S, O]
                                                       ): TypeClass.Aux[S, O] = ???
    
      implicit def default[S <: MyType](implicit 
                                        noOtherTC: Refute[OtherTypeClass[S]]
                                       ): TypeClass.Aux[S, S] = ???
    }