Suma
Suma

Reputation: 34413

How to handle duplicated export of an identical symbol?

Following code imports the same symbol twice, because it imports two different objects which both export it:

object Ext:
  def backwards(s: String): String = s.reverse
  
object A:
  export Ext.*

object B:
  export Ext.*
  
import A.*
import B.*

backwards("Hello")

The error is:

Reference to backwards is ambiguous. It is both imported by import A._ and imported subsequently by import B._

It is the same symbol eventually, therefore there is in fact no ambiguity, but I guess some implementations details of export hide this from the compiler.

How can I solve this?

Motivation: In my project I have several objects which all export a set of useful extension functions so that anyone using any of those objects can use those extensions without having to import them explicitly.

Upvotes: 0

Views: 42

Answers (1)

Mateusz Kubuszok
Mateusz Kubuszok

Reputation: 27535

Just don't export/import the ambiguous definition in one place:

import A.*
import B.{backwards => _, *}

backwards("Hello")
import A.{backwards => _, *}
import B.*

backwards("Hello")

or

object A:
  export Ext.*

object B:
  export Ext.{backwards => _, *}
object A:
  export Ext.{backwards => _, *}

object B:
  export Ext.*

Alternatively instead of skipping, rename it:

object A:
  export Ext.{backwards => backwardsA, *}

object B:
  export Ext.{backwards => backwardsB, *}

It is the same symbol eventually, therefore there is in fact no ambiguity, but I guess some implementations details of export hide this from the compiler.

export just defines new vals, defs, type aliases, etc. Technically it's shortcut for copy pasting every signature and adding a delegation to the original definition. You could have export foo.*, then modify it to skip a definition with export foo.{skipped => _, *} so that you would implement it by hand and - as long as the signature would be the same - code would not notice a difference. So you cannot think of export as of some magic batch alias. It's a codegen. Treat it as such and things like:

trait Foo:
  def bar(baz: Baz): Bar

def addToString(foo: Foo)(str: String): Foo =
  new Foo:
    export foo.{toString => _, *} // generates definitions required by the interface
    override def toString = str

suddenly make sense. It also makes sense why any analysis considers the same definition exported through 2 difference places as distinct - this can change any time and the fact that they refer to the same object in just a current implementation detail.

Upvotes: 4

Related Questions