Proposal: ban relative import paths

1,655 views
Skip to first unread message

Andrew Lytvynov

unread,
Apr 2, 2013, 4:42:31 AM4/2/13
to golan...@googlegroups.com
I don't see how relative imports might be useful, moreover they seem like a bad idea generally.
So, how about making relative imports a compilation error? Thoughts?

Aram Hăvărneanu

unread,
Apr 2, 2013, 4:46:06 AM4/2/13
to Andrew Lytvynov, golang-nuts
They might have been used once for bootstrapping, but now the only
instance of them is in cmd/go/testdata. I am very supportive of this
proposal. Newcomers somehow think they need them and there's always a
pointless ongoing discussion about them. In fact there's one right
now.

From my limited understanding they are not covered by the Go 1
contract so let's get rid of them.

--
Aram Hăvărneanu

Aram Hăvărneanu

unread,
Apr 2, 2013, 5:21:01 AM4/2/13
to Andrew Lytvynov, golang-nuts
After discussion on IRC, more clarification is needed:

1) Proposal is for the go tool to enforce this rule, not to remove
relative imports from the language.
2) Relative imports should still work inside $GOROOT so things like
$GOROOT/test/fixedbugs/bug.+.dir/ continue to work.

The go tool already disallows relative imports outside $GOPATH, so the
proposal is to tighten that rule to disallow everything outside
$GOROOT. Perhaps this is not needed in Go 1.1 because now $GOPATH is
required.

--
Aram Hăvărneanu

Tamás Gulácsi

unread,
Apr 2, 2013, 5:23:15 AM4/2/13
to golan...@googlegroups.com
+1

Everything is clearer without relative imports although first they seem useful - just as unused variables.
(So I think this will induce just the same flames :-))

mortdeus

unread,
Apr 2, 2013, 5:49:11 AM4/2/13
to golan...@googlegroups.com, Andrew Lytvynov
No because there are valid reasons them to exist.

1. They can be nice if there are HELLA files importing a pkg from the current project and people fork your project. In IRC Aram suggested that a script can easily change "github.com/otheruser/pkg" -> "github.com/me/pkg", which I added the fact that go tool fmt has the ability to make this very easy. But taking into a consideration a theoretical project with perhaps thousands of local pkg folders all importing each other. The solution becomes complex and not really user friendly.  

2. Also they can be useful in the situation where you generate go code, compile it, and execute it during runtime dynamically relative to the current project file hierarchy layout. For example gopath/src/user/gogen/gen.go may spit out source code to gopath/src/user/gogen/dynamic which imports a local pkg dependency located in gopath/src/user/gogen/dep. Then gen.go may exec.command("go", "build", "../dynamic").Run -> exec.command(". ../dynamic/dynamic").Output().

In a situation where the generator is a proprietary binary, and the dynamic folder must contain the pkg that compiles with gen.go generated code, we cant partially run the gen.go and stop it right after generating the code to convert "user/gogen" to "me/gogen" before gen.go compiles and runs the executable. We have no control and taking away the ability for go to be used this way is a bad call because go's compilation speed makes this practical in real world development scenarios.  

Andrew Lytvynov

unread,
Apr 2, 2013, 6:13:49 AM4/2/13
to golan...@googlegroups.com, Andrew Lytvynov
1. Having thousands of relative imports might be even more confusing and not user friendly. I'd rather run a command for a forked project than have a possible need of working with multiple relative imports all over the place.

2. This one sounds very strange.
What gives the need to use proprietary generator to spit out code into your package's path?
If you need the generated code itself, just create $GOPATH/src/user/gogen and copy the code over when it completes.

I really find it hard to understand your argument here.
Maybe you could rephrase it, if i got it wrong?

Andrew Gerrand

unread,
Apr 2, 2013, 7:01:07 AM4/2/13
to mortdeus, golang-nuts, Andrew Lytvynov
You can't "go install" a relative import, so if you have a package that uses relative imports you must recompile those packages on every compilation.

This is expensive, particularly in the first scenario you describe (where thousands of local packages import each other). Avoiding precisely this scenario is, in my opinion, a good reason to remove support for relative imports.

It's too late for this to happen in Go 1.1, though.

Andrew


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

mortdeus

unread,
Apr 2, 2013, 7:29:05 AM4/2/13
to golan...@googlegroups.com, Andrew Lytvynov
1. Well Relative imports prevent fetching and building the wrong dependencies when forking on github and using go get to fetch your personal fork for the first time. Like I said this isnt really a complicated issue until you combine it with my next point.
2. Doing something like this http://www.mesa3d.org/llvmpipe.html except its closed source and distributed as a binary executable. If the executable spits out code within the projects base hierarchy, compiles it, and executes the code. There is no way for us to step in and patch the generated code's import path strings to match our forked path. (ie import "github.com/other/gogen/dep" -> import "github.com/me/gogen/dep") If we personally have no way of modifying gogen's source code because it is proprietary then we have to work within the original projects filepath. Using "../dep" makes the project fork agnostic. It also allows gogen to send the dynamically generated code to different locations in the local file hierarchy, thus programmatically allowing us to change which dependency pkg is compiled" gogen -> pkg/foo/dynamic or pkg/bar/dynamic. foo and bar can have their own unique "../dep" pkg.

Trust me I can build on this idea for hours and hours.           

Andrew Gerrand

unread,
Apr 2, 2013, 7:33:29 AM4/2/13
to mortdeus, golang-nuts, Andrew Lytvynov

On 2 April 2013 22:29, mortdeus <mort...@gocos2d.org> wrote:
Trust me I can build on this idea for hours and hours.  

It's possible to conceive of all kinds of bad situations. That doesn't mean we should design for them.

Andrew

mortdeus

unread,
Apr 2, 2013, 7:37:50 AM4/2/13
to golan...@googlegroups.com, Andrew Lytvynov
Why anybody would program like this however is beyond me. The better way for gogen to work is to have a flag that allows the developer to set the path manually or use os.getwd().   

mortdeus

unread,
Apr 2, 2013, 8:35:06 AM4/2/13
to golan...@googlegroups.com, mortdeus, Andrew Lytvynov
Well, it is definitely something that needs to be explicitly defined in the spec because http://tip.golang.org/ref/spec#Import_declarations says that the import system is implementation dependent. Meaning that $GOPATH is completely optional for other implementations of the language.

Also what about cases where we have GOPATH=$HOME/foo:$HOME/bar, and for whatever reason we want to import a pkg in bar from another pkg in bar regardless if the importpath is found in foo. The same applies for goroot and foo.

Making relative paths illegal means we should also define $GOROOT and $GOPATH in the spec. We might as well because without relative imports in a go implementation that doesnt want to force the developer to work within a $GOPATH, all package dependencies must either be stored in the default system library lookup path (/usr/lib), or import strings are relative to the systems root. (import "sync" -> "/sync" or "C:\sync" which is ugly)

While nobody should ever write an implementation of Go like this, we cant really say we didnt allow it.                

mortdeus

unread,
Apr 2, 2013, 8:38:46 AM4/2/13
to golan...@googlegroups.com, mortdeus, Andrew Lytvynov


On Tuesday, April 2, 2013 7:35:06 AM UTC-5, mortdeus wrote:
Well, it is definitely something that needs to be explicitly defined in the spec because http://tip.golang.org/ref/spec#Import_declarations says that the import system is implementation dependent. Meaning that $GOPATH is completely optional for other implementations of the language.

Also what about cases where we have GOPATH=$HOME/foo:$HOME/bar, and for whatever reason we want to import a pkg in bar from another pkg in bar regardless if the importpath is found in foo. The same applies for goroot and foo.

Making relative paths illegal means we should also define $GOROOT and $GOPATH in the spec. We might as well because without relative imports in a go implementation that doesnt want to force the developer to work within a $GOPATH, all package dependencies must either be stored in the default system library lookup path (/usr/lib), or import strings are relative to the systems root. (import "sync" -> "/sync" or "C:\sync" which is ugly)
I forgot to mention importPaths can be set to look in the actual system Path until satisfied.   

Jan Mercl

unread,
Apr 2, 2013, 8:49:34 AM4/2/13
to mortdeus, golang-nuts, Andrew Lytvynov
On Tue, Apr 2, 2013 at 2:38 PM, mortdeus <mort...@gocos2d.org> wrote:

Maybe I don't get it but is seems to me that every relative import
path has at least one equivalent non-relative import path. That's IMO
both necessary and sufficient to prove that relative import paths are
not necessary at all - they should be considered at most as a
convenience only.

-j

Gerard

unread,
Apr 2, 2013, 8:54:53 AM4/2/13
to golan...@googlegroups.com
After reading the initial mail, it made sense to me. After reading the other comments, it made even more sense.

Andrew Lytvynov

unread,
Apr 2, 2013, 8:56:03 AM4/2/13
to golan...@googlegroups.com, mortdeus, Andrew Lytvynov
mortdeus,
In what situation would you have two identical import paths in different elements of GOPATH ?
If someone gets some issues because of such situation, it's most likely his/her own fault, go shouldn't be designed to work badly-configured.
Somehow these arguments sound to me like "what if we want to have some unused variables around our code?"

I don't think any of those are valid reasons to keep relative imports as is.

Updating the spec should not be an issue, if this change is moved to go1.2 or later, imo.

Robert Johnstone

unread,
Apr 2, 2013, 9:44:30 AM4/2/13
to golan...@googlegroups.com, mortdeus, Andrew Lytvynov
This is another version of "all language are Turing complete".  While true, it is unhelpful.  The real question is what is the most effective approach for managing imports?  Does the functionality of relative imports pay for the additional complexity?  Those "conveniences" affect how productive you can be as a programmer.  (Although, conveniences can become so complex they start to detract as well).

Robert Johnstone

unread,
Apr 2, 2013, 9:46:30 AM4/2/13
to golan...@googlegroups.com, Andrew Lytvynov
This argument is very telling.  The language implementers need access to relative imports, but regular language users must do without.

Andrew Gerrand

unread,
Apr 2, 2013, 4:02:16 PM4/2/13
to Robert Johnstone, golang-nuts, Andrew Lytvynov

On 3 April 2013 00:46, Robert Johnstone <r.w.jo...@gmail.com> wrote:
This argument is very telling.  The language implementers need access to relative imports, but regular language users must do without.

Yes. There are many examples of special cases for the language implementation. It's not a conspiracy, just pragmatism.

Should users also have access to 6g's -+ flag? Should the test runner ($GOROOT/test/run.go) be part of the go tool? (note: these are rhetorical questions)

Andrew

Andrew Gerrand

unread,
Apr 2, 2013, 4:03:50 PM4/2/13
to mortdeus, golang-nuts, Andrew Lytvynov
There's no reason to push this stuff into the spec. This discussion is about the design of the go tool, not the language.

Why wouldn't someone who wrote an alternate language implementation just use the go tool? It's written in Go. They should be able to just compile and use it, inheriting its conventions.

Andrew


--

Kevin Gillette

unread,
Apr 2, 2013, 7:44:16 PM4/2/13
to golan...@googlegroups.com, mortdeus, Andrew Lytvynov
Ignoring any stdlib/runtime pragmatism, the only important use-case I've seen for non-url imports is in dealing with multi-package public project code in forks. For example, if I fork <github.com/bradfitz/go-smtpd> into <github.com/extemporalgenome/go-smtpd>, especially if my intentions extend beyond authoring a simple pull request, right now I'd have to change all bradfitz import references in the 'ajas' subpackage to be 'extemporalgenome' references. iirc, relative imports are already forbidden for being used for this purpose (and even if they were usable, they'd still be a fairly inelegant solution).

I think this is still a problem that, while relative imports could have solved them, should not be used to solve them. Ignoring potential implementation ambiguity, a better human-understandable approach would be to introduce (in the implementation, not in the langspec) a notion of a "project root", and require all paths be either absolute w.r.t. GOPATH/GOROOT, or absolute w.r.t. the project root. For "github.com/bradfitz/go-smtpd/ajas", the project root would be "github.com/bradfitz/go-smtpd", and from within ajas, an import of "//smtpd" (or some other syntax besides '//', preferably not involving '..') would refer to "github.com/bradfitz/go-smtpd/smtpd" for Brad's original, and in my fork, would refer to "github.com/extemporalgenome/go-smtpd/smtpd" in the case of my fork.

In terms of implementation, most of the time it'd just "work" for known code-hosting systems (and would be unavailable for use in the stdlib). The cases where ambiguity would crop up would be like "labix.org/v2/mgo" -- without being a supported code hosting system, or without having some manifest file (which IMHO would set quite a bad precedent), there'd be no way for the go tool to know if "labix.org/v2" is a static prefix (and that "mgo" itself is a project), or if "v2" or even the "/" is a project. Doing a parent-traversal of trajectories can find reasonable candidates, but simply creating the directory "labix.org/v2/mgo/v2" could completely alter the behavior of importing "/v2" from within the mgo package. Because of this, even organizationally saner approaches can have prohibitive implementation obstacles.

Robert Johnstone

unread,
Apr 2, 2013, 8:14:16 PM4/2/13
to golan...@googlegroups.com, Robert Johnstone, Andrew Lytvynov
There are special cases for the language, and, yes, my observation does not prove relative imports should allowed.  However, it is still an interesting observation, and still deserves an explanation about why relative imports should be restricted as a special case.  An explanation you (or no one else) has provided.

I'll ignore your comment regarding 6g's flags or test runner, since it is a straw man.

Robert Johnstone

unread,
Apr 2, 2013, 8:18:04 PM4/2/13
to golan...@googlegroups.com, mortdeus, Andrew Lytvynov
In an earlier thread, I had suggested that relative imports be restricted so that parent (../) references be disallowed.  This would mean that relative imports could only be used to access sub-packages.  I don't know if the suggestion received much attention.

Andrew Gerrand

unread,
Apr 2, 2013, 8:47:07 PM4/2/13
to Robert Johnstone, golang-nuts, Andrew Lytvynov

On 3 April 2013 11:14, Robert Johnstone <r.w.jo...@gmail.com> wrote:
an explanation about why relative imports should be restricted as a special case.

See my earlier post in this thread about why relative imports are a bad idea.

The only reason the go tool supports relative imports is to run the compiler tests, which predate the go tool and the concept of GOPATH. I would prefer to refactor the compiler tests so that relative imports are not used, and remove support for relative imports from the go tool entirely.

Andrew

Robert Johnstone

unread,
Apr 2, 2013, 10:35:59 PM4/2/13
to golan...@googlegroups.com, Robert Johnstone, Andrew Lytvynov
The explanation for why the go tool supports relative imports makes sense, but it still does not explain the resistance to allowing them for general projects.

You may not be able to go get a relative import, but certainly I can import a package with an absolute path.  If that package used relative imports, for example to reference sub-packages, the go tool could determine absolute paths that are required without too much difficulty.  The go tool would translate relative imports to an absolute import before checking that the package was up to date, building, etc.  To emphasize, even though you might reference a package with a relative path, there would be no binaries marked with that relative path, only the absolute path obtained by combining with the current package.

There is a genuine use case for relative imports when used to reference sub-packages.  This issue has certainly caused me to de-factor packages to avoid the need to constantly update import statements.  And no, this was not a trick to maintain dead code, but a consequence of collaborating on a package, each of us with our own repository.

The issue of relative imports has recurred frequently.  My belief is that the issue rises over and over again is that no one has yet supplied a solid technical reason to say no.  The arguments have either (a) involved believing any proposal for relative imports would also require allow some pathological use case or (b) stating that the implementation would be overly complex, and we don't want to do it. 

The argument that the developers are unwilling to invest the time is perfectly reasonable in my view.  They are certainly free to choose their priorities, and code walks, but no one is going to submit a patch while they expect that it would be rejected out of hand.

Andrew Gerrand

unread,
Apr 2, 2013, 10:47:13 PM4/2/13
to Robert Johnstone, golang-nuts, Andrew Lytvynov
On 3 April 2013 13:35, Robert Johnstone <r.w.jo...@gmail.com> wrote:
The explanation for why the go tool supports relative imports makes sense, but it still does not explain the resistance to allowing them for general projects.

You may not be able to go get a relative import, but certainly I can import a package with an absolute path.

That shouldn't be the case. Packages should be imported by one canonical path, and that's it. Go's import path scheme was designed to provide one global name space for import paths, where each package exists in one canonical location.
 
There is a genuine use case for relative imports when used to reference sub-packages.

There is no such thing as a "sub-package". Each package should have only one canonical import path.
 
And no, this was not a trick to maintain dead code, but a consequence of collaborating on a package, each of us with our own repository.

Why not instead collaborate using the same central repository. It works well in my experience.
 
The issue of relative imports has recurred frequently.  My belief is that the issue rises over and over again is that no one has yet supplied a solid technical reason to say no.

I disagree. I think it's because we've done a poor job of communicating the benefits of a single global name space for import paths, and that people only want to do things the way they have in other environments.
 
  The arguments have either (a) involved believing any proposal for relative imports would also require allow some pathological use case or (b) stating that the implementation would be overly complex, and we don't want to do it. 

My argument is neither a nor b.

I think the solution is better tooling support for renaming imports, and better education about the correct way to use the go tool.

Andrew


Robert Johnstone

unread,
Apr 3, 2013, 12:02:17 AM4/3/13
to golan...@googlegroups.com, Robert Johnstone, Andrew Lytvynov
I think that you should reread my previous post.  You say that every package has one canonical path, and my explanation supports that.  You say that the packages should live in a global name space, and my explanation supports that as well.  You say there is no such thing as a sub-package, and I say that net/http is a sub-package of net.  I apologize if my previous message was unclear, but your response is mostly irrelevant since it is based on misunderstanding (or mischaracterization).

Without understanding the relative merits of allowing (or not) relative imports, it's doubtful you'll have much success educating anyone.

David Symonds

unread,
Apr 3, 2013, 12:52:41 AM4/3/13
to Robert Johnstone, golang-nuts, Andrew Lytvynov
On Wed, Apr 3, 2013 at 3:02 PM, Robert Johnstone
<r.w.jo...@gmail.com> wrote:

> I think that you should reread my previous post. You say that every package
> has one canonical path, and my explanation supports that. You say that the
> packages should live in a global name space, and my explanation supports
> that as well. You say there is no such thing as a sub-package, and I say
> that net/http is a sub-package of net. I apologize if my previous message
> was unclear, but your response is mostly irrelevant since it is based on
> misunderstanding (or mischaracterization).

net/http is not a sub-package of net. Those two have no special
relationship at all. Indeed there was a time when net/http was just
"http". Their relative positioning in the standard library is a matter
of organisational convention; it does not engender anything beyond
that.

Rob Pike

unread,
Apr 3, 2013, 12:58:07 AM4/3/13
to David Symonds, Robert Johnstone, golang-nuts, Andrew Lytvynov
Nothing is going to happen before Go 1.1 is out the door. Let's suspend this conversation until that happens.

-rob


Robert Johnstone

unread,
Apr 3, 2013, 9:12:53 AM4/3/13
to golan...@googlegroups.com, Robert Johnstone, Andrew Lytvynov
Boy is my face red!  I had thought that the package namespace is hierarchical.  :)

(Sorry Rob, I couldn't resist)
Reply all
Reply to author
Forward
0 new messages