How can we override/shadow Predef.conforms


While re-factoring some code I realized that a local implicit is being substituted
by Predef’s identity. More concretely I have the local definitions:

val defaultB = false

implicit def isDefault(v : Double) : Boolean = v.isNaN
implicit def isDefault(v : Int) : Boolean = v == defaultI
def isDefault(v : Boolean) : Boolean = v == defaultB
implicit val conforms = (v : Boolean) => isDefault(v)

I have a class that uses these implicitly:

sealed trait Algorithm {
  def convert[T](command: String, value : T)(implicit check : (T) => Boolean) : List[String] = {
    if (check(value)) List() else List(command, value.toString)

When searching for a solution I found:
Accordingly as shown above I have redefined conforms using a def and val and
declaring the function as shown above or as a standard function. However when I
try to use this in the class:

case class General(shrinking: Boolean = defaultB) extends Algorithm {
  val shrinking_ : List[String] = convert("-h", shrinking)

I get the error:

[error] adw/src/main/scala/com/github/cjlin1/SVM.scala:248: ambiguous implicit values:
[error]  both method $conforms in object Predef of type [A]=> <:<[A,A]
[error]  and value conforms in object SVM of type => Boolean => Boolean
[error]  match expected type Boolean => Boolean
[error]     val shrinking_ : List[String] = convert("-h", shrinking)

Can anyone tell me how I can ensure that my version of the function is used?


Shadowing conforms will not do anything, because it is no longer the name of the implicit.

However, I would not rely on this “shadowing” mechanism in general, if I were you. You can shadow the <:< and =:= (which are implicit identity functions), but you cannot shadow the whole mechanism of conformance, because it is beyond implicits.

So I would say the answer to your title question is “better not”.

Instead, I suggest getting out of the implicit conversion space entirely. You can introduce an IsDefault typeclass with instances for Double, Int, or what have you, for example.

final case class IsDefault[-A](test: A => Boolean)

implicit val isDefaultDouble: IsDefault[Double] = IsDefault(_.isNaN)
implicit val isDefaultInt: IsDefault[Int] = IsDefault(_ == defaultI)

I would say “beware contravariance and pray for contrarivariance one day”, but you are already subject to the foibles of contravariant typeclasses (as that is how you are using implicit conversions), so would be no worse off in that respect. And you would be considerably better off, having partitioned yourself off into a space where you can be sure that only what implicits you mean will be selected.

1 Like

Cannot pretend to understand all those references but I do grasp the solution you give. Thank you.