Also, having helped a number of people now through their early days of the language it's also the one biggest thing by far that reliably confuses every single person. And I mean everyone — from first time programmers all the way up to people who've been in the industry for decades and are learning Go as their tenth language. Being forced to put files in certain places is incredibly non-intuitive because there's nothing else out there that requires it. I must have sent the Go documentation on workspaces [1] to two dozen different people at this point.
But on the other hand, once you've grasped the system and are using it, `GOPATH` is surprisingly not bad. It's always obvious where your dependencies are located and which versions are going to be used to build your project. Even better, it lets you very easily drop into those dependencies and add minor changes or debugging lines if you need to. This can be incredibly useful if you're trying to understand how one of them works or think that that you might have found a bug and trying to verify or patch it. A very powerful feature once you know about it.
The new Go modules seem good, and will be a huge improvement in lowering the barrier to entry for Go, but I'll miss the old `GOPATH` style of work at this point.
> It's always obvious where your dependencies are located and which versions are going to be used to build your project.
More obvious than specifying the version in some dependencies file?
> Even better, it lets use very easily drop into those dependencies and add minor changes or debugging lines if you need to.
What dependency management system doesn't allow you to do this if you insist?
I have nothing against dependency files, but yes, it's probably even more obvious.
If I see an `import "github.com/x/y"` at the top of a Go file, I know that I can go find the source for that at `cd $GOPATH/src/github.com/x/y` (unless the project has a `vendor` directory in which case you'll look in there instead).
> What dependency management system doesn't allow you to do this if you insist?
I'm sure you could do it on anything, but Go makes it very easy.
As noted upthread, it's pretty simple to do this in other languages like Ruby when you're using Bundler and know the system well (e.g., `pushd $(bundle show excon)`), but in many languages it's much more difficult. You'd need to go chase down the source, download it, then redirect your dependency to point to it. Sometimes you'll grabbed a newer version that's no longer compatible so you'll have to make sure to work off of the right tag.
In bad cases (e.g., C, Java) you'd have to learn how to build the project which might not be trivial because tooling isn't standardized or convenient to use.
Dependency management systems that require you to hunt for where they stashed whatever they sucked down based on the versions you listed in some file might, in some cases, make this particular task more complex than it strictly needs to be.
I've done this quite a bit with typical Ruby+bundler setups. I think the key is that both Go and Ruby start from source code, vs. something like Java where your dependencies are compiled JARs without source (of course, there is infrastructure to enable easy fetching of the source when available).
This is similar to my experience with Nix package manager. It's purely source-based with binary packages seen as something like a compiler cache. It's easy to get the source to a package, make some tweaks, and build (and use) a custom version (at least I personally have found it far easier than Debian, even with apt making it easy to get the source).
You should totally check out Roger Peppe's gohack[1] It enables you to do the same in a Go modules world
The main difference would be that all dependencies are also in this folder, instead of e.g. a maven or ivy folder located somewhere else. And a (suggested?) folder structure, not dissimilar to java's package structure, where you'd put your e.g. github.com/user/repo repository into $GOPATH/src/github.com/user/repo. I'm sure there's clever tooling or commandline wizardry that works really well with a structure like that.
GOPATH is really not a problem, but efficient concurrency is. Go made the company I worked for go from 20 deployments for a service to 1 (I/O bound), reducing the costs for that service a lot. If you are willing to let the environment get in between that...
Of course, you could have implemented that system using assembler for all I care, but Go really made it possible within that organization, something Java, C and other languages failed at before Go was introduced.
N=1, ymmv, &c
Java's concurrency solutions are just as good, if not better than golang's. Pair that with libraries such as RxJava and you're definitely ahead. I don't see what golang has to offer in that area that the JVM doesn't
We were using C because epoll made it possible for us to compete, but the Java reactivex stuff were lagging behind.
Both of those things are selling points for me.
While I disagree with GO's style guide and a ton of other choices - I don't mind following them since that's what the language requires. But it goes past that and wants me to organize everything my working directory just for it - it's absolutely crossing a line.
While I didn't stop using Go because of GOPATH, it's one of those things that absolutely annoyed the crap out of me.
to the point that all my go projects are now organized as `project_name/go/src/github.com/username/project_name` as an example.
This was really really annoying to say the least.
If you are investing time into learning a new language anyway, these things take very little time in comparison and definitely worth it.
I find it so much better experience than using tons of `~/Work`, `~/Projects`, `~/Code`, `~/SomeLang/` etc. as I used to have before.
Just wanted to point this out as a clear example of such an issue, since it doesn't come up very often so cleanly.
> Go 1.11 adds preliminary support for a new concept called “modules,” an alternative to GOPATH with integrated support for versioning and package distribution. Using modules, developers are no longer confined to working inside GOPATH, version dependency information is explicit yet lightweight, and builds are more reliable and reproducible. https://golang.org/doc/go1.11#modules
That sounds extreme. I've never heard this before. Why?
def my_very_very_very_very_very_long_function_name(self,
param1,
param2): def my_very_very_very_very_very_long_function_name(
self, param1,
param2):
pass
Shrug. Seems okay to me, and preferable to what they say no to. def my_very_very_very_very_very_long_function_name(
self, param1, param2):
... def my_very_very_very_very_very_long_function_name(
self,
param1,
param2,
):
do_it() # function body here.The main ones that get me are the whitespace (4 characters) and restrictive line length (79 characters) rules. They don't work well together, and also don't work well with python's significant whitespace (can't easily break up a line), namespaces, generator expressions, and type hints. All for a subjective formatting decision.
I'll also often see people trying to cram as much as they can into 79 characters with one letter variable names and such while calling that elegant code because it's a "one-liner."
To be honest it says right at the beginning of PEP-8 that "A Foolish Consistency is the Hobgoblin of Little Minds" but it seems little minds are the norm (and usage of 'pep8' package to make developers do what a computer could do faster and better)
> This document gives coding conventions for the Python code comprising the standard library in the main Python distribution.
If you are not writing Python's stdlib, you are free to not to stick to it.
If in doubt, the next section of the document is helpfully titled "A Foolish Consistency is the Hobgoblin of Little Minds".
With a constructor, I can force data to conform to what I need it to. I can ensure that certain fields are not nil, that they conform to a specific list of values, etc.
Because of a lack of a constructor in Go, I can't do that and I need to continuously validate the data, which is annoying and a source of bugs. It can be said that this can be accomplished with interfaces but that's adding a lot of complexity to something that should be a lot easier to handle, in my opinion.
If you’re in a different package things aren’t too bad if you have meaningfully abstracted interfaces. And with Go’s duck typing, even if you’re using another package that doesn’t have good abstraction, you can create your own interfaces with the functionality you need for mocking purposes.
1. Error handling is difficult - they don't return anything, so you are forced to use exceptions, and throwing exceptions in constructors is awkward. Go doesn't even have exceptions so it can't do that.
2. They don't have names. Often this is fine - you only have one way to construct an object. But if you have more than one, then you have two unnamed functions that do different things. And if they have the same parameters you end up with crazy workarounds like adding dummy parameters.
The way Rust does it is far superior. Basically you have a static function that creates the object. It's a normal function, so it can return errors (or the object), and you can name it, so you can have `Circle::new_with_radius(float r)` and `Circle::new_with_diameter(float d)` with no confusion.
Much better.
I hear language complaints like this all the time, and it always sounds like developers grasping for something to complain about.
I'm not even a Go fan, but I think you can cut the language some slack here at least in this department.
The entire approach Go is selling from what I've hear is they're entirely forgoing the traditional object-oriented (in the C++ and Java sense of the term) approach for composability. Okay, sure, whatever, good for Go developers; but you can't complain about a leopard having spots. Just accept it or move on.
It's not; presumably that's the point. C isn't exactly famous for its support for data hiding and enforcing invariants.
I assume what upsets people is that everything in go is in a global namespace. So if you have ~/foo-project with some go in it, that doesn't work; you can't import things from there and the compiler won't build it. Instead you have to position it in the "global" ~/go/src for anything to work.
I used to teach Perl classes and people were equally upset about the concept of @INC. They did not want to manage packages that way, and the programming language did not give them a choice. This is very off-putting to some people.
(Me, I don't care. Not having 8000 configuration options to let the compiler find some github project I'm using is wonderful, even if having to cd go; cd src; cd github.com; cd jrockway; cd project; is kind of a lot of typing to get to the thing you're working on. I have a bash alias to get there ;)
In addition to your bash alias for navigation, you can use the `autocd` option of bash so that from your home directory you can say `cd /project`.
You can also use the CDPATH variable for quick navigation into a deep directory structure like that.
Keep the actual directory where Go wants it to be and create a symlink to it in ~/dev/$who/$project.
Next, create a script that you name as “mygo” or whatever (something short and memorable that makes sense to you. I would probably name it as just “g”) and put it in your ~/bin/ and ensure you have ~/bin in your $PATH.
In said script you resolve the real path of your project, cd there and then execute /usr/local/bin/go with the args that your script got:
#!/usr/bin/env bash
cd "$( realpath . )"
/usr/local/bin/go "$@"
So when you are in ~/dev/someclient/someproject/, you run “mygo build” and the script runs “go build” from the real path of the project. (At first I suggested to name your script as just “go”, but I decided that it was probably better to use a non-colliding name instead and so I quickly edited this comment.)That ought to do it.
I totally agree with you though. I do similar to you — I keep public projects under ~/src/github.com/ctsrc/$project and client projects under ~/src/$client/$project. If it wasn’t for the fact that I don’t write in Go I would be annoyed too.
FTA: "Go modules are great, and probably the single best module system I’ve used in any programming language. Go 1.11 took my biggest complaint and turned it into one of my biggest compliments."
But… I don't like Go itself (anymore).
It's not just the 'if err != nil', it's more the general attitude/philosophy from which it comes. Go is anti-intellectual, the designers basically don't respect the user. "You're too stupid to use generics/Result<A,B>/monads/whatever" is what it feels like they think of you.
Go internals are more infuriating than Go language though…
Especially the Plan9-based custom assembler. It truly is a horrific atrocity. I've had to deal with it two times:
1. That assembler does not support all instructions and even addressing modes (!) of amd64, so people have had to create hacks like https://github.com/minio/c2goasm to run asm functions without the overhead of cgo. Please actually read that project's README, but tl;dr it assembles your code using a normal assembler and STICKS THE BINARY CODE INTO A GO ASSEMBLY FILE AS HEX CONSTANTS. This hack actually didn't work for me when I tried to use some SIMD code, so I had to resort to cgo with its call overhead.
2. I also tried (and failed) to port the Go runtime to FreeBSD/aarch64. This was the last straw, this is what made me actually hate Go.