How to resolve "reference to func is ambiguous" for DSL


#1

Not sure if this is more of a design issue… but here goes. I am trying to develop a ggplot2-like DSL. I have the following structure that I am using:

object ThingA {
  trait X
  case object X1 extends X
  case object X2 extends X

  def func(a:X) : X = X2
}

object ThingB {
  trait Y
  case object Y1 extends Y
  case object Y2 extends Y

  def func(a:Y): Y = Y2
}


import ThingA._
import ThingB._

object Ambiguous {
  def main(args: Array[String]): Unit = {
    println(func(X1))
    println(func(Y1))
  }
}

And I get the error:

[error] Ambiguous.scala:25:13: reference to func is ambiguous;
[error] it is imported twice in the same scope by
[error] import ThingB._
[error] and import ThingA._
[error]     println(func(X1))
[error]             ^
[error] Ambiguous.scala:26:13: reference to func is ambiguous;
[error] it is imported twice in the same scope by
[error] import ThingB._
[error] and import ThingA._
[error]     println(func(Y1))
[error]             ^

The idea is that func is part of the grammar that can be used in various contexts (for X's and for Y's).
So my questions are: is their any way I can get around this? If not what’s the best way do organize the code?

As an aside, why can’t the compiler resolve this? The IntelliJ IDE points to the correct implementation.
In addition to this, if I place all the func in the same module, then it works.

TIA

P.S: same result for Dotty


#2

In theory this could work, if there was some sort of overload resolution for all names in scope. I’m not sure why they chose instead to always report ambiguity in this case. But it’s probably more complicated than meets the eye. I think overload resolution is already pretty tricky.

If possible you could go with something like this:

trait ThingA {
  trait X
  case object X1 extends X
  case object X2 extends X

  def func(a: X): X = X2
}

trait ThingB {
  trait Y
  case object Y1 extends Y
  case object Y2 extends Y

  def func(a: Y): Y = Y2
}

object myDSL extends ThingA with ThingB


object Application {
  def main(args: Array[String]): Unit = {
    import myDSL._
    println(func(X1))
    println(func(Y1))
  }
}

#3

@Jasper-M
I have been staring at your solution and mauling it over. My first thought was: will I be painting myself into a corner with this? The other though was, the last object declaration you used in effect places all func into the same module - same as I had done in a previous test. However I realize yours is a better solution because I keep the code split into manageable parts.

I will be “mauling” some more and see if I can put it into practice.

Thanks.