Package object scope while implicit lookup

When I have a package object defined with implicit types in the same package where I intend to use I don’t have to write any explicit import statement, which is pretty convenient. However, if I have package object defined in any of other parent object then implicit lookup doesn’t go there unless I explicitly import that package object.

e.g.

Package Object:

package com.mycomp.myproj.myservice
package object dao {
  
  implicit def toXyz(str: String): Xyz
  
}

Target Class 1:

package com.mycomp.myproj.myservice.dao
object test {
   val xyz : Xyz = "str" //this works

}

Target Class 2:

package com.mycomp.myproj.myservice.**myrepo**

object test {
   val xyz : Xyz = "str" //this doesn't works

}

Why wouldn’t implicit lookup go to the package object defined in parent packages? what harm it could have?

Implicit scope includes the package objects of enclosing packages. “implicits defined in a package object are part of the implicit scope of a type prefixed by that package.”

Not sure it makes sense to include arbitrary child package objects?

Reason I raised this question is I am ending up defining package object for each package since visibility of type defined in package object is limited to only that package and not its child package. It just look ugly to have a package object for each package (modules). Moreover it reduces code reusability. You end up having package object in each package to have it imported automatically. Package object seems to be designed for defining common type aliases, implicits, functions, fields etc. Writing explicit import statement to import such package object seems to defeating it’s purpose. I could just as well defined all that in regular object and import it.

From the docs:

Package objects can even inherit Scala classes and traits.

with one caveat:

Note that method overloading doesn’t work in package objects.

But this still might allow you to at least reduce clutter for your use case (granted it will still require creating multiple package object files).

I wonder who wrote that and why. From what I can see overloading just works…

There is support for chained package clauses:

package myorg
package service

will result in the definitions of the myorg package and myorg.service packages being visible in that module.

Certain groups consider this poor style. I consider this a very useful feature for package hierarchies.

So am I correct in saying if you define packages as

a.b.c.d

then you have to import package objects into each subpackage, whereas chained package clauses gets around this problem - but is considered bad style. If so, what is the best practice for package wide implicits etc? - I’m currently trying to share some common code but find I’m importing it into every file, so not really benefitting.

(I read that Dotty does away with package objects).

Sorry to revivify, but I promise to inject fresh content or context.

I happen to be tweaking Scala 2 messaging for package objects, was bored and noticed an unread topic.

Dotty has deferred deprecating package objects (which are replaced by “top-level definitions”), but no longer supports looking up implicits in prefixes of a type which are package objects.

To review, implicits are searched for twice, first in lexical scope (enclosing class and package), and if that fails, second in “implicit scope”, which means parents of a class and also of type args, so for List[C], companion objects of List and of C. Implicit scope of an inner class C#D includes companions of both D and C. If the full name was p.q.C, it includes packages p and q, which means their package objects, in Scala 2. The packages in the prefix of C are dropped for Scala 3.

2.13.12 warns under -Xsource:3 if a package object extends a class, because of an earlier expectation that the feature would be removed, and that dotty would remove package objects altogether. Today’s tweak is to make it warn only for actually disappeared feature, namely, package prefixes in implicit scope.

Enclosing packages as lexical scope are unaffected, but the usual rules of visibility apply, as described in this thread.

To answer the question about style, excessively nested package clauses can indeed make it difficult to see where a named class comes from, let alone an implicit. For example, a common package name like util might be problematic: first, you wonder which package does util refer to; second, it might safely refer to a particular package but not after changing package clauses.

To say more, with imports, you get precedence and shadowing, so you can introduce widgets.util when you need it.

I hoped to find some guidance, but I see scala.collection.immutable demonstrates all permutations.

Some files used to say

package strawman
package collection

after the experimental or development name, so it’s partly for historical reasons.

I’d propose standardizing via a formatting tool that project files have a certain header and a certain top-level package, below which components are permitted to innovate. That opinion is not based in science, but in years of annoyance accumulated in the nervous system like a toxin.

To illustrate the question about overloading, this works with an ordinary object and import, but not with a package object:

trait T {
  def f(i: Int) = i.toString
}
trait U {
  def f(s: String) = s*2
}

package object p extends T with U
package p {
  object Test extends App {
    println(f(42))
  }
}

errors with

pkg-overload.scala:12: error: type mismatch;
 found   : Int(42)
 required: String
    println(f(42))
              ^
1 error

wait, let me try

package q {
  import p.`package`._
  object Test extends App {
    println(f(42))
  }
}

which works, so just look-up from inside the package is broken in Scala 2.

Everything works in Scala 3, except the traits must be in a package, for visibility.

There may be other problems, for example, with incremental compilation.