As far as I know there is no shared trait in the collections library that defines the map
method (most likely because there are different signatures for map
).
I have an observable value (think of a property in a ui system) that has a change event. The observable values can be mapped using a map
method.
When we are however working with a type that already has a map
method, we should be able to use the built-in method of map
.
So instead of:
prop map { x =>
x map { actualX =>
//do something
}
}
I want to use it like this:
prop map { actualX =>
//do something
}
I have a simplified test case. First the different parts I am using:
// leaving out the observable part
trait ObservableValue[T] {
def value: T
}
trait LowerPriorityImplicits {
// default implementation that adds a regular map method
implicit class RichObservableValue1[A](o: ObservableValue[A]) {
def map[B](f: A => B): ObservableValue[B] = new ObservableValue[B] {
def value = f(o.value)
}
}
}
object ObservableValue extends LowerPriorityImplicits {
// describe a type that has a map method
type HasMapMethod[A, Container[X]] = {
def map[B](f: A => B): Container[B]
}
// complex implementation that uses the builtin map if present
implicit class RichObservableValue2[A, Container[Z] <: HasMapMethod[Z, Container]](
o: ObservableValue[Container[A]]) {
def map[B](f: A => B): ObservableValue[Container[B]] =
new ObservableValue[Container[B]] {
def value = o.value.map(f)
}
}
}
If there is something (or maybe a lot) wrong with the above code let me know. I want to use it like this:
class TestCase extends ObservableValue[Option[Int]] {
def value = None
}
val x = new TestCase
x map { value =>
// this fails because the compiler finds the lower priority implicit
(value: Int).toString
}
// the method itself works fine
ObservableValue.RichObservableValue2(x) map { value =>
(value: Int).toString
}
If I change Container[B]
into Any
it will find the RichObservableValue2
implicit conversion.
My knowledge about how types are used to select implicits is limited.
I tried to find the answer in the following locations, but the subject is a bit overwhelming:
Is there anyway to solve this challenge?
Edit
I know about the FilterMonadic
trait for collections. I am looking for a solution that recognizes the map
method as defined in the Option
class.
Edit 2
It seems the FilterMonadic
variant is not working either. I added RichObservableValue3
to the RichObservableValue
object.
implicit class RichObservableValue3[A, C[Z] <: FilterMonadic[Z, C[Z]]](o: ObservableValue[C[A]]) {
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[C[A], B, That]): ObservableValue[That] = new ObservableValue[That] {
def value = o.value.map(f)
val change = o.change map (_ map f)
}
}
And again that implicit conversion is not chosen although a List[Int]
is a valid argument. I must be missing some rule that is used when selecting implicits.