HNHacker News
TopNewBestAskShowJobs

Merovius

729 karma · joined June 14, 2015

submissionscomments
Merovius··on Go Modules in 2019
> So instead of creating M packages * N OSes, we do it just once.

No, creating M packages x N OSes is exactly what we do. In fact, we create M+N package-managers x N OSes - and then also create MxN packages.

If you can write a language-specific package manager for all OSes, you can also write a non-language-specific package manager for all OSes, so I the "it doesn't work in all distributions" argument is just a symptom of my complaint. Instead of working towards a cooperative packaging solution where the effort of packaging can be re-used, we continue to create more and more special snowflakes and fragmentation.

Merovius··on Go Modules in 2019
> Saying that 'maven central' implies maven isn't decentralized would be the same as saying "Most go projects are on github, so go can't be hosted in a decentralized way because github is 'golang central'".

It's not, though. Because github isn't the default. You have to explicitly specify that you want a project from github every time you use it.

The difference might be minor from a technological standpoint, but it means a lot in terms of how centralized things become culturally.

Merovius··on Go Modules in 2019
> Allowing single line if statements (as, eg, ruby does) also allows for clearer code, at the expense of consistency.

This (or rather allowing conditionals without braces) was what I was referring to. They are a foot-gun, because of the lack of clarity. It's a common source of bugs in C/C++ code that a developer thought they'd add a line of code to a conditional block or loop but didn't, because of a lack of braces. Requiring braces unconditionally make it always unambiguous and obvious what block a given statement belongs to.

Obviously YMMV - this is, as most discussions in programming, a matter of opinion. Which was my point :)

Merovius··on Go Modules in 2019
Note that there's a difference between "central registry" and "central index". In particular, I don't see any downsides from what they are changing - godoc.org is already, for all intents and purposes, a central index, so I don't see how anything changes in that regard.
Merovius··on Go Modules in 2019
> Same applies to Maven, in fact many enterprises forbid access to Maven central

"Maven central" implies there is a central repository though.

Merovius··on Go Modules in 2019
This is exactly why I personally think efforts for language-specific package management in general are misguided. This is a solved problem in general-purpose package-managers (apt, dnf, pacman,…), for all their flaws. I don't understand why we need to re-invent that over and over again, including all the duplication of effort to re-package everything over and over again…
Merovius··on Go Modules in 2019
> The best course of action for Go on this front, would be to expose operator overloading of numeric operators, combined with a community expectation that this isn't to be used for anything but numeric operations.

I don't think this solves the issues. For example, I have yet to think of a way to overload `+` in a consistent and performant manner. E.g. think of `a += b` vs. `a = a + b`. Either you make them separate operators (in which case there's no guarantee they do the same thing - see for example Python, where they commonly differ) or you are overallocating in the common case.

Merovius··on Go Modules in 2019
> Basically before go 1.11, it was impossible to just do

That's not true. It was always perfectly possible to do that, the code you build just couldn't import any libraries not installed in GOPATH. AIUI that is exactly the same as in other languages.

Merovius··on Go Modules in 2019
> I think a primary problem with the modules workflow is that you can't switch to it until all Go projects you work on switch to it

It seems this gets fixed in Go 1.12 though. AIUI you'll be able to set GOMODULES=on, unset GOPATH and just continue working as before.

Merovius··on Go Modules in 2019
> "golang" is the official disambiguation, so it's factually incorrect to call people out for using that term.

I don't think "factually incorrect" is warranted. It's factually correct that the language is called Go. And while "golang" is a useful alternative for searchability and where "go" is taken, it is still a valid criticism to request using "Go" in natural language and prose. As for "official disambiguation", I'd say the most official thing said on the topic is this FAQ entry, which is quite clear on the matter: https://tip.golang.org/doc/faq#go_or_golang

(not saying Brad's comment isn't abrasive or rude. But it's not "factually incorrect")

Merovius··on Go Modules in 2019
> ironically, the Go team has named their own/list forum "golang-nuts" despite that "not being the name of the language"

FWIW, go-nuts was already taken.

Merovius··on Go Modules in 2019
> Pike seems especially aggressive when interacting with people he disagrees with and this was for me indicative of the culture. It's been a few years though, so maybe by now I'm wrong!

FWIW, as much as I agree with Rob Pike on most things, I totally agree that he's abrasive and his way of phrasing things is often unhelpful (even though I think people also tend to read contempt into his brevity that isn't there). If it helps, I think he has realized that in the meantime and is largely staying out of public discussions for that reason.

Merovius··on Go Modules in 2019
> Have a problem with that? Expect a snarky comment about your priorities.

Whereas your comment is completely devoid of snark.

Merovius··on Go Modules in 2019
> It is most certainly not a preference for clarity, though. It is a preference for consistency.

FTR, with "most certainly" you're committing the same fault your accusing the Go team here. You might not think it matters for clarity. I, at least, disagree.

> in our experience most people can't be trusted to not shoot themselves in the foot.

I don't understand the difference between this suggested phrasing and talking about clarity. I'd argue you tend to shoot yourself in the foot if and only if you aren't clear about what you're doing. Clarity and lack of footguns seem directly correlated.

Merovius··on Using roughtime as a “cryptographic notary”
> without the centralized authority (cloudflare in this case) problem?

I don't consider it a problem. But FWIW, you can use an arbitrary set of servers - all this is establishing is a relationship between some public keys and the hash. So there isn't really a centralized authority. I would even argue that it's tolerant to byzantine failures, given that the servers cross-certify responses, so misbehaving servers can be weeded out with high probability.

I don't think there is an advantage to using a blockchain but there downsides - and as CiPHPerCoder mentions there are a lot of those.

Merovius··on Using roughtime as a “cryptographic notary”
As haxton says: a) go-get (fixable), b) didn't know (unfixable), c) fun (working as intended) :)
Merovius··on What else is in Go 1.11?
Ideally, CI won't fail on invalid format, but just politely apply the correct format. There's nothing more frustrating than being told to do a job a machine can trivially to better (like executing "gofmt").
Merovius··on A response about dep and vgo
> Minimal Version Selection. In contrast to pretty much every other package manager, vgo tries to install the oldest version of a packages that still satisfies any lower bounds specified anywhere in the dependency tree.

This is somewhat misleading. Most package managers use a human-edited manifest containing constraints and a machine-edited lockfile, containing the chosen versions. You periodically (or after changing dependencies to require newer versions) run a tool that fetches the newest versions satisfying your constraints and writes out a lock-file, which you then commit and use at install-time to get reproducibility.

Go modules use a single file that serves as both and is both human- and machine-edited. You periodically (or after changing dependencies to require newer versions) run a tool that fetches the newest versions satisfying your constraints and updates the manifest with them. The manifest is then (also) used at install-time with minimum version selection to get reproducibility.

What that means is that in practice, Go modules never install older versions than the equivalent in other package managers would do. The maintainer specifies constraints and uses a tool to decide what version will actually used to at build-time. In both cases, that tool will choose the newest versions available.

What is correct (and contentious) is that Go modules have no concept of upper bounds for dependencies. So the complaint is, that as a library author you can not prevent a newer version from being chosen by one of your reverse dependencies. It remains to be seen how much of a problem that will be in practice.

So, if anything, the problem is that as opposed to other package managers, Go modules sometimes choose too new versions, from the perspective of some people. It never chooses too old versions.

Merovius··on Russ Cox on Go dependency management
> The point is that I don't have to change any of my source code in order to change what registry I point at.

Apparently it's a matter of opinion, but I consider that a feature, not a bug. It means the import path, as the identifier of the package, identifies the package. If two source files refer to the same name, they mean the same code.

> code has no place dictating where it's downloaded from, yet go packages do that.

That is still not actually correct. The webserver listening on the domain is dictating where the code is downloaded from. i.e. the owner of the identifier of the code you are downloading.

The basic assumption is, that if you take some code and put it somewhere else, you are assuming ownership, so you should actually own the identifier of that code. If someone wants Library X, they should be sure to actually get Library X, not your fork Y.

> I can't use the go package management ecosystem if my canonical import path doesn't resolve (e.g. dep won't work, go get won't work, vgo won't work).

To me, this seems to be a weird and unfair comparison.

gopkg.in exists. There is nothing in the Go ecosystem that is preventing what you want to have. The equivalent to Rust's cargo+crates.io isn't go-get+custom-domain, it's go-get+registry. And the equivalent to the import path "github.com/user/package" isn't (import of the "package" crate plus a mirror-directive in your cargo config), it's just the string "package". Because that's the identifier of the crate.

You could make the argument that Go still doesn't allow you to have mirrors, which is fair on the one hand, but will be solved with modules on the other.

Really, IMO it's just a fallacy to think of the import path as a download location. It's an identifier and the only reason the domain must resolve, is because go-get must consult the owner of that identifier what code to fetch. And DNS is the canonical tool to do that. Just like you need to resolve a hostname to get a Let's Encrypt certificate or use the Google Webmaster tools - you have to prove ownership of the name.

Merovius··on Russ Cox on Go dependency management
The closest to that is https://sdboyer.io/vgo/intro/ and https://sdboyer.io/vgo/failure-modes/. IMO these posts are hard to digest and ultimately unconvincing. But YMMV.
Merovius··on Russ Cox on Go dependency management
> If so, why not just call it a version conflict and fail until it's resolved?

Because resolving it, in general, doesn't scale. If the dependency graph is

A -> B -> D

A -> C -> D

B and C have to coordinate their upgrade to Dv2, so as to not break A. But B and C might not know of each other and A might not even know about their dependencies on D. Worse, C might not be able or willing to upgrade to Dv2 in the foreseeable future, for lack of bandwidth, so B is kept from upgrading to Dv2 or has to break A.

If you allow both Dv1 and Dv2 to coexist, B can upgrade to Dv2, A stays unbroken and at some point, C can upgrade to Dv2 too. No need to coordinate the timing or undue rush.

Now multiply that graph with a hundred dependencies and a thousand projects A, and it becomes clear that avoiding the need of coordinated upgrades is a good thing.

Merovius··on Russ Cox on Go dependency management
> If I have a canonical import path of "mydomain.io/foo", and I host html there pointing to github or whatever, I now have a different single point of failure: the domain. If I lose control of the domain or the

> Most package management systems have one additional layer of resolution: a centralized pluggable repository location which can be configured to some default, and then lookups are made against that instead of against a url specified in the source code.

TBH, these two seems to contradict each other. You don't want to use your own domain, because that's a SPOF that you could lose control over. Whereas the centralized repository is a SPOF that you never had any control over to begin with. Seems strictly worse to me. And the nice thing about the chosen approach is that it can coexist with what you seem to be wanting: gopkg.in exists and is exactly that. A centralized, hosted (and free!) registry for go packages with its own registration and authorization process.

What I find so beautiful about the go-get solution is a) that you can build any other mechanism I know of on top of it - i.e. the user can decide on their own tradeoffs of convenience/scalability/security/future-proofedness… and b) that domain names already provide an elaborate, globally agreed upon namespace that can separate ownership from administration and hosting, with its own takedown-semantics… It's decentralized, existing, robust infrastructure. It gives full control to the user. And it solves a whole bunch of technical challenges already, for free. It means the Go team doesn't have to manage user accounts of package maintainers and it doesn't have to have policies of what code to accept or not accept - as it can't enforce them.

It's really quite brilliant, IMO.

Merovius··on Russ Cox on Go dependency management
> While that works I think it would have been a better idea to keep package names and storage locations separate

Then why didn't you? Go had that ability since the inception of go-get. You could have used your own domain to name your packages from the beginning and have avoided that.

Merovius··on Go’s hidden pragmas
> Except you're having to implement them yourself.

This is plainly wrong. All components I mentioned exist in the stdlib.

You have to glue them together yourself, sure, but that's the point of having components with separated concerns, which is usually considered a good thing in software engineering.

> There is zero reason why I should be having string together code from four different modules to accomplish this myself.

You don't. You have to use at most 2. And also, to repeat the question: who cares? Like, what is the actual downside of having to import 2 packages? I also took the liberty of looking for solutions to how to do this in other languages. Here is a Java solution, which is in line with what's requested that has 4 imports and one of them is third-party: https://stackoverflow.com/a/1096859. Here's rust code with 4 imports: https://users.rust-lang.org/t/read-a-file-line-by-line/1585. Python and Haskell get away without imports; because they just make reading files a language-builtin/part of the prelude, which TBQH is pretty cheaty.

Like, even if I'd buy into the notion that modularity and composability are bad things, it's not even as if Go would be in any way an outlier here. And even if it where then at best this is the mild complaint that no one has yet wrapped this in a ~10-line library; the language certainly does allow it, contrary to what's claimed.

I'm sorry, but this complaint is just forcibly trying to make up a problem where none exist to fit your narrative of Go being a bad language. It's not productive.

Merovius··on Go’s hidden pragmas
> In this case, I suppose my specific complaint is that I'm unable to make `range` work transparently with an arbitrary type.

Sure, fair enough. But note that you've now shifted the criticism from "I have to import 4 packages" (which was wrong) over "I need to know implementation details of packages" (which was wrong) to "I don't like that Go doesn't have operator overloading".

> I want to think of a file as a black box full of strings.

And what exactly is preventing you from doing that? Like, how exactly is the language preventing anyone from providing this much higher level API? You could even make it work with range, if you so desire (it would be considered very unidiomatic, but presumably you don't care).

The stdlib provides you with composable pieces to achieve the job you want. I still find this complaint incredibly weird, unless you assume that everyone wants to view files as just a bunch of lines (I'd argue, these days, the overwhelming majority of files probably aren't). Like, you will still need the lower-level API; where's the problem with having an stdlib which focuses on providing composable pieces and then having some library do the composition for higher-level concerns?

Last time I checked, having small composable units of code with clearly separated concerns was pretty much universally considered a good thing.

> it's how the Reader API works, and whether I read a full or a partial line

This is just a random aside, but: You never have to care about that, unless you specifically want to. But I would argue that code that calls io.Reader.Read is likely wrong - unless it does so to wrap it. Use io.ReadFull.

Merovius··on Go’s hidden pragmas
> Here's how I, at least, would like it to work:

I still don't get what your problem is. It seems what you want is to just take an io.Reader and pass that to bufio.NewScanner, solving your problem and letting your caller figure out what Reader to pass you? I mean, to me, this seems to be a solved problem and exactly one of Go's major strengths.

> it makes it difficult to decouple modules since code often has to be at least somewhat aware of quite a few implementation details of a library in order to use it correctly.

You still haven't described a single piece of your code that requires, in any way to know any implementation details of any of the libraries you are using. Like, you don't have to care how os.File is implemented, it just gives you a Read method that you can use to read from it, just like a thousand other Readers. And then you can use that in a bufio.Scanner to read lines (words, whatever tokens), without that having to care in any way about how the Read method is implemented. You want to scan lines from a byte-slice, use bytes.Reader, that's it's sole purpose and your scanning code does not have to care what Reader it gets passed.

Like, I seriously don't understand your problem here. It would seem to me, what you are describing is exactly how Go works.

> "How" a piece of code functions is at best the third, and probably only the fourth most important question (behind "why", "what", and probably "when" if you use any concurrency at all), but Go forces it to be front and center at all times.

Sure, I agree that Go does not encourage you to build deep abstractions. But I fundamentally disagree that you have to know any implementation details - anymore than any other language. Yeah, the type system doesn't lend itself to build extra abstractions, but "having to care about implementation details" just is not one of the symptoms of that o.O

Merovius··on Go’s hidden pragmas
I hate this kind of "if only you'd write large programs and not just toys, you'd understand how bad Go is".

I write Go at work. I haven't counted in a while, but I guess the code of my team comprises significantly more than 200k lines (and that doesn't even count all the code I'm working on that isn't from my immediate team).

IMO, the more LOC you have, the more the design of Go becomes a godsend. Because it becomes much easier to dive into code you haven't written yourself.

But anyway, that's just my opinion, I just wish that people would stop this "anyone who disagrees with me just doesn't have the correct set of experiences" FUD.

Merovius··on Go’s hidden pragmas
> you end up having to frequently write type-switches which are checked at runtime to do any sort of generic code.

This baffles me. I think I basically never use any type-switches, with the exception of interfaces being used as a sum-type - in which case the problems you mention with type-switches just don't come up.

> Case in point: to do something as painfully simple as reading a file, you need to import bufio, io, io/util, and os.

I don't know what you mean here. `ioutil.ReadFile` reads the whole file, done. Even if you prefer linewise-scanning, you still only need `bufio` and `os`.

But even if you'd need all those packages to read a file: So what? Like, I honestly don't understand what's the problem with that.

Merovius··on Common Gotchas in Go
Sure. It's an easy problem to fix, now that the team is aware it. I'm pretty sure they'll solve it for Go 2. In the meantime, it would break the compatibility promise, so it's just a kludge we have to live with.

I basically just wanted to point out that the problem isn't so much an implementation question (or about performance). But that no one thought of it and it's now mainly a question of how to best phrase the spec for this :)

Merovius··on Common Gotchas in Go
> It just feels like I basically never want it to be a copy, but you do frequently want to modify the element you're iterating over.

Feels differently to me :)

> It actually pushes you to design things in ways you might otherwise not. E.g., instead of []X you'll have []* X just so it'll be easier to modify from inside a loop.

Never do that. Instead I use indexes when I actually want to access the element.

(Where I do do that is in maps, but not because of range, but because index-expressions over maps are not addressable)

← PreviousPage 3 of 8Next →