Reputation: 19485
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
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