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.
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] = ???
}