Home > Software design >  What is the simplest way to create given for A & B type?
What is the simplest way to create given for A & B type?

Time:12-27

I have

trait Foo:
  def foo: Int

trait Bar:
  def bar: Int

given Foo with
  def foo = 1

given Bar with
  def bar = 1

And I have a function foobar

type FooBar = Foo & Bar
def foobar(using FooBar) = ...

What is the simplest way to create given for A & B type if I already have given A and given B

CodePudding user response:

You could get Foo & Bar by nesting using clause in given instances but once you start to modify the values in FooBar the results might not be what you expect because a FooBar is also a Foo and Bar and things starts to get recursive:

trait FooBar extends Foo with Bar

given (using f: Foo, b: Bar): FooBar with
  def foo = f.foo   1
  def bar = b.bar   2

def fooBar(using fb: Foo & Bar) = (fb.foo, fb.bar)
def foo(using f: Foo) = f.foo
def bar(using b: Bar) = b.bar

@main def main() =
  println(foo) //2
  println(bar) //3
  println(fooBar) //(3, 5)

IMO you should avoid subtyping relationship with typeclasses and define FooBar without extending Foo and Bar:

trait FooBar:
  def foo: Int
  def bar: Int

given (using f: Foo, b: Bar): FooBar with
  def foo = f.foo   1
  def bar = b.bar   2

CodePudding user response:

Admittedly I have no direct Scala 3 experience, but the simplest solution will boil down to something like:

given aAndB(using a: A, b: B): A & B with {
  def foo: Int = a.foo
  def bar: Int = b.bar
}

You're effectively gluing together an A and a B and dispatching appropriately.

It's possible that one could implement a macro which dispatches (when there are no collisions) automatically.

AFAICT, this would be the equivalent of the Scala 2:

implicit def aAndB(implicit a: A, b: B) = new A with B {
  def foo: Int = a.foo
  def bar: Int = b.bar
}
  • Related