T. Stone
T. Stone

Reputation: 19485

Scala implicit conversions that do not apply to certain types

I have a container type that holds metadata associated with a value.

class Container[A](val value: Option[A], val meta: Metadata)

Sometimes it's useful to access the meta but most of the time it's more convenient to work with a value as if it was Option[A]. There is an implicit conversion for this.

object Container {
  implicit def containerToValue[A](c: Container[A]): Option[A] = c.value
}

All of that works fine. The issue is that often there will be an implicit conversion present from A => B and the value happens to be Container[A]. What I'm looking to do is to provide a way to do an implicit conversion basically from Container[A] => Option[B]. My first attempt was something to the effect of:

object Container {
  implicit def containerToValue[A](c: Container[A]): Option[A] = c.value
  implicit def containerToB[A,B](c: Container[A])(implicit conv: (A) => B): Option[B] = c.value.map(conv)
}

Scala didn't like that at all, so as a fallback since in 90% of the cases B will actually be a String, I tried doing something with more of the types filled out:

object Container {
  implicit def containerToValue[A](c: Container[A]): Option[A] = c.value
  implicit def containerToB[A](c: Container[A])(implicit conv: (A) => String): Option[String] = c.value.map(conv)
}

This did a little better but I got a number of errors about ambiguity. What I was wondering is if there was a way to specify that A is not String. Perhaps something like:

implicit def containerToB[A >!> String]

Such that I could tell the compiler this implicit conversion applies only when A isn't String.

Is there a better way to handle this?

Upvotes: 0

Views: 75

Answers (1)

wheaties
wheaties

Reputation: 35970

Instead of doing an implicit conversion (because they're hard to debug, cause runtime errors when you least expect them, and make the code generally less clear) why not add a type class to the mix so that you can work with things the way you want to work with them?

trait Extractor[A,B]{
  def opt(that: Container[A]): B
}

and then you explicitly create all the various instances that you might want to have:

def myGloriousFunction[B](that: Container[A])(implicit ex: Extractor[A,B]) ={
  val value = ex.opt(that)
  //more stuff
}

It'll handle the finding of the "extractor" for your inner Option and it's still fairly explicit in its signature.

Upvotes: 1

Related Questions