Home > front end >  Scala add extension method when the type params have a common type
Scala add extension method when the type params have a common type

Time:11-23

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
    }
}
  • Related