Also, at the beginning the Go team wasn't all that enthusiastic about an IDE, but its great to see that Go will get an IntelliJ grade IDE soon, so that works for me :)
Also, at the beginning the Go team wasn't all that enthusiastic about an IDE, but its great to see that Go will get an IntelliJ grade IDE soon, so that works for me :)
With Go's removal of version numbers, they haven't removed any of the complexity of resolving incompatibilities, they've just hidden the fact that there is one. I would have much preferred a strategy of maintaining version numbers, and resolving them via "either you specify what version you want pulled in, or we'll build with the most recent and throw a warning".
It's fine to not ship a solution in version one. But to use it as a reason to not have version numbers at all seems like heading in the wrong direction (backwards).
https://nodejs.org/api/modules.html
// Package A:
import B from "B"; // A/node_modules/B/1.0.0
import C from "C"; // A/node_modules/C/1.0.0
// Package B:
import D from "D"; // A/node_modules/B/1.0.0/node_modules/D/1.0.0
// Package C:
import D from "D"; // A/node_modules/C/1.0.0/node_modules/D/2.0.0Now let's say that module A is depending on version 1.0.0 of module C, and module B is depending on version 1.5.2 of module C.
Does B.bar(A.foo()) still work? Is type t the same in C version 1.0.0 and 1.5.2?
B = import("b", ">1.0")
C = import("c", ">1.1 && <2.0")
etc.1. Treat each version of a package as a separate package. If there are two versions required, they are both included.
2. At compile time transform package names to include the desired version number.
I may be completely misunderstanding or over-simplifying this problem.
Imagine a queueing library, for instance. You push something onto a queue created with library A, and then you want to read from library B (A and B are different version of the same library). Just about anything could happen (depending on how the library/language are implemented). You might get an error due to library B reading a data structure formatted for library A. Or you might get nothing back, because library B is checking for the queue in its own registry, while it exists in library A's. Etc.
If there is no library-dependent state between calls, or the calls are not being mixed between libraries, you can totally do that, though. A JSON serialization library can totally be supported in that manner, for instance. The queueing library example, if it's only being used to support other libraries or separate functionalities, such that you never have to push onto a queue in library A, and expect to be able to pop it off from a queue in library B, would work, too. But in that latter case, that's not something the library writer can guarantee, it's all about your own usage. So there's a lot of potential issues.
// package a
lib-v16.Queue(int32) // Stores in queue for version 16 of lib
// package b
int32 = lib-v18.DeQueue() // Loads from queue of version 18 of lib, which is empty
And even if we accept that you shouldn't have state in the package and the queue should be passed around, now we just have two packages (a & b) that use incompatible versions of lib for potentially no reason.To expand on (b), suppose that packages A and B depend on a shared queue managed by package C, as in your example. Because A and B have such tightly coupled logic, chances are that some package--either one of A and B, or some other package that depends on A and B--will have code that expects a type from "A's C" to be equivalent to a type from "B's C". At that point the code will fail to compile, and the programmer will fix the problem before it ever hits production. (Fixing this is easy because you can just inspect the lockfile to find all the crate versions in use.) Basically, static typing makes it such that, while the problem exists in theory, in practice its probability is so low as to not be a concern.
This won't work if the dependencies are truly depending on incompatible-at-the-API-level versions of the same library, of course. But there's no magic bullet solution to that—not handling versioning in the language/package manager doesn't make that problem any easier.
But that requires a monorepo that scales and people to test and check in the upgrades, so it doesn't work well for open source development, which is why people are working on alternatives.
But this can all be done using a separate system. The build system itself doesn't need to know anything about versions. If you look at Bazel, it doesn't let you specify versions for target deps. Only the developer actually upgrading a third-party library needs to deal with open source version numbers, and everyone else can build on their work.
Of course it could be done in a separate system. It could be done and tracked in a separate system even with version numbers. Go has just chosen to -require- you running your own package manager to make things stable, whereas for most languages it's optional and you can create a repeatable, portable build without it.
The diamond dependency problem only happens when two indirect dependencies are identical but on different versions and that these two versions are incompatible. That's two big if's. 99% of the time, it's not a problem.
Notice first that this is a pretty rare problem since libraries tend to be backward compatible these days. And if you happen to come across a bad behaved one, you simply exclude the one you don't want from the graph, problem solved.
This problem has been fixed in Maven for almost ten years now, the only reason why you would not implement this crucial feature in a version manager is laziness.
For better or worse, a sizable proportion of developers choose IDEs on the basis of looks.
https://github.com/go-lang-plugin-org/go-lang-idea-plugin
So far the best "Go IDE", I've used.