There is one alternative that I don’t think has been raised yet: parameterize import. If it looked like a method with a single implicit parameter, then you could
The disadvantage of this is that import is not a method, relative and absolute should not actually be in the symbol table in scope, and even if it was a method you can’t pass arguments after a method name without parens.
The advantage, however, is that it’s extremely obvious what is going on because ( and ) are not valid package names and visually separate the behavioral “parameter” from the imports, and because you can choose names that are actually meaningful in context. Furthermore, it’s possible to define more than two, if you want; so far we have discussed three behaviors (relative, absolute, and preferAbsolute). But there are others. For instance, you might want to import only symbols that aren’t already in the table, e.g. import(ifMissing) java.util._ to avoid shadowing scala.collection.immutable.List.
@Ichoran The only issue with this proposal would be that it cannot be used in an expression position. Otherwise, I like the idea of having at least import[relative] and import[absolute].
Using x.y in an expression is not just about shadowing. It’s for when you want to use x.y without importing everything from x. As @nafg explained, that’s how you access fields and methods.
A fully qualified access like .my.foo.bar avoids worrying whether my is shadowed or not, so you can use it even when nothing is shadowed.
FWIW, a starting dot reminds me of ::std::whatever in C++ — the initial :: also means “from the root”.
Let me spell it out for stronger effect:
In any language design, the total time spent discussing
a feature in this list is proportional to two raised to
the power of its position.
0. Semantics
1. Syntax
2. Lexical syntax
3. Lexical syntax of comments
EDIT: I also enjoy relative imports, also from subpackages. Simplified example from actual code, off the top of my head:
package ilc
package feature.booleans
import feature.sums //relative to ilc
//... define booleans in terms of sums ...
> let mapmap f = List.(map (fun xs -> map f xs)) ;;
val mapmap : ('a -> 'b) -> 'a list list -> 'b list list = <fun>
// for:
> let mapmap f = List.map (fun xs -> List.map f xs) ;;
val mapmap : ('a -> 'b) -> 'a list list -> 'b list list = <fun>
It looks rather confusing to me. And I guess this would only work for cases where literally everything you need is a member of the same path. For instance:
object foo {
val a = 1
val b = 2
val c = 3
}
def bar(f: (Int, Int) => Int) = foo.{ f(a, b) + c }
I think the only consistent way to desugar this would be foo.f(foo.a, foo.b) + foo.c.
Then I would just prefer the full form with the import. In foo.{ f(a, b) + c } it looks like you are prepending foo. to every term. That’s also how the import foo.{ ... } syntax works.
This discussion inspired me to add ("_root_." . 46) to prettify-symbols-alist when in scala-mode and my use case (which sound like yours?) is pretty much fixed.