Hacker News new | past | comments | ask | show | jobs | submit login
Golang 1.4 Internal Packages (golang.org)
132 points by signa11 on June 25, 2014 | hide | past | favorite | 47 comments



It would seem the important feature of a modern language is how it handles modularization. The ability to program-at-large requires a language to make trade-offs here and there.

I do note that the Go-centric world would naturally make the file system part of the language. Its plan9 heritage makes this very easy.

I also note that this is a mistake in language design. Packages and modules should never be reliant under an assumption about the underlying file system, as it tend to lock languages to certain platforms.


> Packages and modules should never be reliant under an assumption about the underlying file system, as it tend to lock languages to certain platforms.

And it gives you an escape hatch from Java-style module nonsense: of COURSE I want to traverse 7 singleton directories to get to the source code!


Java allows you to put all your source files in one directory and javac *.java will happily compile them. Package-directory structure applies only for compiled classes and internal content of a jar file. For Java sources, that is just a convention.

But of COURSE, it is so modern to blame Java for everything these days.


I had no idea. Why do so many projects setup these crazy directory trees for source then?


Because it allows you to have your User.java class in both com.comp.ldap and com.comp.db packages.


Blaming java is nothing new.


That would be a simple fix to be honest - and one that Java 8 could make without backward compatibility issues. Simply allow the hierarchy to be short cutted if a directory contains the a dot separated name

e.g instead of com/foobar/bar/module/a/b allow com.foobar.bar.module/a/b


I believe Java will behave sanely with regard to source directories. If you have a file Foo.java declaring itself as "package x.y.z", then it can happily live as "src/Foo.java".

It's only when you try to run the thing that the JVM will complain (with the rather unhelpful error message "wrong name: x/y/z/Foo"). So if your build process builds the correct folders and stores the .class files there, it's going to work. (Not that it's very useful; after all, your build system would have to know where to put everything.)


The fact that we are comparing all of this with java makes me so sad.


Which happens to be shared by other languages as well.


Had the Go team made an assumption, they may have leveraged filesystem case-sensitiveness (or rather, case preservation†) to follow the same rule as for package exports: capitalised => exported, lowercase first => not exported.

The only assumption here is that the filesystem supports a tree.

† This means any current filesystem except FAT which has (had?) a habit of changing the case of same-cap names in one way or the other. Cap-changing renames are a chore on some systems though.


> Packages and modules should never be reliant under an assumption about the underlying file system

They are not. Import paths are not part of the language, they are interpreted by the go tool. The only relation to the filesystem is that by using the go tool, you will get a directory hierarchy that seems to match import paths in the sense that that in foo/bar you will have a bar directory child to a foo directory. This is very useful to developers, it means they can find their files, but you could build a conforming build tool that keeps files in SQL, if that's your thing.


This is not a language change. It is a new convention enforced by a standard build tool. The compilers/linkers are not affected, nor is the language specification.


Yes, the restricted macro preprocessor known as the go tool is not part of the language, but like the compilers and links it is part of the Go toolchain.

The proposed changes are onerous because integrating with the toolchain to enable this feature requires changes to source code.

Moreover, the proposed change is so very tied to a specific toolchain feature that it is likely that as the Go team encounters new package dependency management issues that they will continue to propose these micro tweaks to package import paths which require the rest of us to run around touching every source file.


"Onerous"? Of all the Go source code on GitHub, there was only one place where an /internal/ path component wouldn't have the desired effect of this change.

If you're talking about using this feature with existing code, renaming a directory is not a hard operation.


The Go language specification states that the package import path is an opaque token.

The Go team has decided to interpret that as an opportunity to interpret these tokens as an expression language with the following capabilities: locate packages on the filesystem, locate packages from some specific version control systems, (proposed) control how packages are compiled.

My beef with this change is two-fold. First, why is there no specification for the package import path when there is plain, obvious, and necessary information encoded in this opaque token. Second, why is expediency a good justification for including compiler control flags in this undefined, unspecified opaque token.

If the package import path is going to be used like this, then it should be standardized in the language specification.


The answer to your first beef is that it would complicate the specification unnecessarily. You would suddenly have to provide a lot more details in the language spec, such as defining what capabilities a filesystem must provide, environment variables, OS and architecture details, package formats, compiler invocation syntax, and so on. That's all taken care of by the go tool right now, and doesn't burden other compiler implementations with all that.

Expediency is not used as a justification for this at all. You're wrong that any compiler control flags get involved here; 6g is going to be invoked with exactly the same syntax, and doesn't get told anything about "internal", just as it isn't told anything about build tags, or OS/arch-specific filename control (e.g. foo_linux.go).


And I think that's jlouis' point, that internal packages could and should have been implemented at the language level, not the tool level.


Given that a package can consist of multiple files, how do you purpose to do that? Each file in a package declares "package namegoeshere" at the top, so you would need to add a keyword to every file, or have some very confusing/weird behavior of partially unexposed/exposed packages.


Indeed. This is done better and with more care in Standard ML.


Magic directory names seems a bit of a hack to avoid supporting this notion in the language itself.

AFAIK this also introduces the English language into the currently freeform world of user import paths. I guess source files themselves already have something similar with the "_test.go" suffix, so there's some consistency with that.


Plus, every keyword is already in English.


That goes without saying at this point. I'm not saying the English-in-path thing is a huge deal, but it's worth noting effects that sneak in along with a feature.


What magic directory names? The special case for "pkg" is the only one I saw.

The main proposal is one of scoping by directory hierarchy - /a/b/c can import a/b/d but a/g/x can't.


> /a/b/c can import a/b/d but a/g/x can't.

No - x can import d because d isn't in a directory literally named internal. That's the magic name.


> What magic directory names?

"internal"

> The main proposal is one of scoping by directory hierarchy - /a/b/c can import a/b/d but a/g/x can't.

/a/b/c can import a/b/d, and /a/g/x and /z can too. The new rule is that /a/g/x can not import /a/internal/y.


> The new rule is that /a/g/x can not import /a/internal/y.

As I understand it, /a/g/x CAN import /a/internal/y, because /a/g/x is in the directory tree that starts at /a/. However, /b/h/z CANNOT import /a/internal/y/ (while previously, it could).


The way ML modules works seems to me like a much better deal: with interfaces and implementations. There's a proposal to have something similar done to Haskell.

[1] http://plv.mpi-sws.org/backpack/


Just like most module based languages.

What ML modules have as extra advantage over other module systems, is that they also exist as types.


In Go you can expose an interface, and often in the standard library they do.


>There is unexported code duplicated in the standard library because sharing it would have required exporting those functions: net/http and net/http/httputil share a few parsing functions, and there are four ‘func itoa’ in various packages. Similarly, cmd/nm and cmd/objdump contain the same file reading code.

This is so basic and has been solved by other languages so long ago that it's quite strange to see Go had to reach 1.4, and 5+ years of age to get this...

...and in quite an ad-hoc and hackish way -- which means you're tied to a directory hierarchy.


You say ad-hoc and hackish, I say simple and practical.


Simple is not always good.

As the quote (attributed to Einstein IIRC) goes: "You must strive to make things as simple as possible -- but no simpler than that".

This is, I think, something "simpler" than simple ("simpleton"?).


Which languages have solved this problem?


All languages that don't tie visibility rules to the name of the variable/function? Even C++ applies IIRC.


The mechanism for specifying visibility has nothing to do with the issue.

The Java approach for standard packages is nearly identical to what's proposed for Go. Anything in the com.sun. (or whatever they call it now) namespace is considered internal, even though it's publically accessible.


The discussion[0] on the golang-dev for this proposal is getting the most discussion out of of any of the proposed features. Most of it are people asking for addtions to the feature that would change/add-to the language syntax (which isn't allowed in Go 1.x) or changing the 'internal' directory name to something else.

[0] https://groups.google.com/forum/#!topic/golang-dev/_cAggq73y...


Backwards compatible changes to the language is allowed in Go 1.x.


I find this solution very Go-like. Akin to other name-dependent features like: Exported functions and types in a package start with a capital letter. Test functions are in '*_test.go' files. Simple and practical, with only one way to do it.


>I find this solution very Go-like.

As in "without deep thought", "ho-hum" and 30 years below the state of the art?


The Go spec has always indicated that package import paths are opaque strings, and that the semantics of interpreting those paths is performed by the go tool.

That said, transforming the package import path into a half-assed file system query language slash dependency management solution is a source of frustration for a problem that has been elegantly solved by DNS.

I would love if the Go team redefined package import paths as strings to be resolved by go-dns -- an imaginary package name resolution tool which looks up package imports by resolving paths via records stored in package, workspace, and upstream "nameservers".

Or, you know, use Godeps.


Isn't this similar to the ancient "private protected" visibility level [1] of Java?

[1] http://everything2.com/title/private+protected


Nice to see Go adopting the subpackage/module notion of other module based languages.


I sound like a broken record, but Golang is totally worthless to me until $GOPATH is abandoned. I don't want everything down to my workspace structure determined for me. Once that's done with I think Go would be an amazing language to use.


GOPATH drives me crazy. I'm the only one of my co-workers really pushing for golang, but all have said that until GOPATH, dependencies, and versioning get 'fixed' they won't even bother with it.


I'm a borderline Go fanboy, but GOPATH tends to drive me nuts as well. It'd be nice if it had a sane default. 99% of the time, I'm just doing GOPATH=$PWD.


I'm glad I'm not the only one confounded by GOPATH. I have a script I use to build my files that does

    GOPATH=`pwd`:$GOPATH go build $1.go
...where my bash_profile initially exports GOPATH as /usr/local/go. Probably unconventional but hey, it works.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: