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

1

3

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.

Barrister answered 14/2, 2020 at 19:45 Comment(1)
@LuisMiguelMejíaSuárez I can reproduce gist.github.com/DmytroMitin/88ec5c4c9d8ae6ccbba37898c1f66f5eLinden
L
4

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] = ???
}
Linden answered 14/2, 2020 at 20:36 Comment(2)
Thanks Dmytro. Altough I had already solved my case by using a hand-made Not typeclass I was hoping there was a general way to define priorities. I though the general way was using a LowPriority trait, but I wasn't aware of the relative specificity rule :-/Barrister
@Barrister Well, there is no such thing as "priority of implicit". You can't change "priority of implicit" from 100 to 101 or 99. Just for every two candidates you have weight of the first over the second 0, 1 or 2 and weight of the second over the first 0, 1 or 2. Using compiler internals you can manually infer the process of implicit resolution and then you can do whatever you want but generally it's not a good practice.Linden

© 2022 - 2024 — McMap. All rights reserved.