Search code examples
scalaobjectimportpackagelibrary-design

How to Do Nested Imports


I am trying to clean up my user api, so that users can neatly just import from a single package object, and get all the stuff inside. I realize I can just move my packages to sit in the top package, but I was wondering if there is a way to do the following (i get it may not be best practice):

I have the following packages:

package org.Me.packages.packageA

case class A() {
// some implementation
}

package org.Me.packages.packageB

case class B() {
// some implementation
}

Now this would require imports of the form:

import org.Me.packages.packageA.A
import org.Me.packages.packageB.B

What I was requested to do was the following:

package org.Me.combinedPackages

package object Combined {
   import org.Me.packages.packageA.A
   import org.Me.packages.packageB.B 
}

So that my end user can simply do:

import org.Me.combinedPackages._

// access both a and b here

from what I read here, I understand it to mean nested imports are not possible.

So is what I am trying to do impossible? I realize other ways exist.


Solution

  • In Dotty what you want to do would be possible via export (dual to import):

    package org.Me.combinedPackages
    
    
    export org.Me.packages.packageA.A
    export org.Me.packages.packageB.B 
    

    In Scala 2 however you would have to:

    • use a type alias for every type you want to "pass on" as accessible from your package
    • use a val for every (companion) object that you would like to redirect via your package
    package my.package
    
    // imported as my.package.exported._
    package object exported {
    
      type NormalType = some.other.package.NormalType
      val NormalType: some.other.package.NormalType.type =
        some.other.package.NormalType
      
      type ParametricType[F[_], A] = some.other.package.ParametricType[F, A]
      val ParametricType: some.other.package.ParametricType.type =
        some.other.package.ParametricType
    
      // be careful to not import things that are automatically imported
      // e.g. implicits in companions, and avoid importing the same implicit
      // twice from two different packages
      implicit val redirectedImplicit: some.other.package.SomeImplicit =
        some.other.package.SomeImplicit
    }
    

    Just in case, I'll just mention that this breaks for e.g. macro annotations, as if you have such alias in macro annotation type, compiler won't recognize that it should have handled that using the original type's macro expansion.