I have a type Foo[ A, B]
and a type Bar[ B]
. I want to add extension method bar
to Foo
like this
implicit class FooSyntax[A](foo: Foo[A, Bar[A]]) {
def bar: A = ???
}
So the extension method is only applicable when B
is a Bar[A]
. And thus all of the following should compile —
trait Fruit
trait Apple extends Fruit
trait Banana extends Fruit
def foo0: Foo[Apple, Bar[Banana]] = ???
def foo1: Foo[Apple, Bar[String]] = ???
def foo2: Foo[Apple, Bar[Apple]] = ???
def foo3: Foo[Apple, List[Apple]] = ???
foo0.bar // Fruit
foo1.bar // Object
foo2.bar // Apple
For my use-case I want to be able to do this without FooSyntax
using an implicit requirement, that way the method is always available and is easy to discover, however calling it is only possible when the type condition is satisfied. This is what I have come so far —
trait Foo[ A, B] {
def bar[C <: A](implicit evA: A <:< C, evB: B <:< Bar[C]): C
}
Needless to say, this doesn't work.
CodePudding user response:
All you need to do is remove the type bound from C
and just leave the implicit evidence to do its job.
trait Bar[ B]
trait Foo[ A, B] {
def bar[C](implicit ev1: A <:< C, ev: B <:< Bar[C]): C = ???
}
Then you can do this (as expected):
foo0.bar // Fruit
foo1.bar // Object
foo2.bar // Apple
You can see the code running here
CodePudding user response:
trait Foo[ A, B] {
def bar(implicit ev: FooBar[A, B]): ev.Out
}
trait Bar[ B]
trait FooBar[-A, -B] {
type Out
}
object FooBar {
type Aux[A] = FooBar[A, Bar[A]] { type Out = A }
implicit def x[A]: FooBar.Aux[A] =
new FooBar[A, Bar[A]] {
override type Out = A
}
}