13 ms·
The author spent a lot of time dwelling on Window's filesystems, at which point many of the readers got bored and started commenting. There are actually a coupl
by fhood 7y ago
The author spent a lot of time dwelling on Window's filesystems, at which point many of the readers got bored and started commenting. There are actually a couple of excellent points in here, the majority of which relate to Go's tendency to just be silently completely wrong in its behaviors from time to time, and is absolutely packed with hidden gotchas.
- whatever_dude 7y agoThis. I like, and agree, with the conclusion, and wish more people would get to it: > Over and over, Go is a victim of its own mantra - “simplicity”. (...) > It constantly lies about how complicated real-world systems are, and optimize for the 90% case, ignoring correctness. > This fake “simplicity” runs deep in the Go ecosystem. I've always liked simplicity and on my own design, I tend to go for abstraction; trying to make it easier for consumers of my API. But nowadays more often than not I find myself preferring to be explicit about the underlying idiosyncrasies when needed. This is partly due to my recent experiences with Rust, and this post seems to concur: > Rust has the opposite problem - things look scary at first, but it's for a good reason. The problems tackled have inherent complexity, and it takes some effort to model them appropriately. In that sense, I especially like the approach to `Permissions`/`PermissionsExt` that Rust takes. It makes it clear what the tradeoffs are, and allows consumers to implement their own high-level, abstracted API without compromises.
- twic 7y agoA post about fixing Date in JavaScript got me thinking about why it took so long for languages to get good date/time APIs. I think it's because it took so long to accept that date and time really is complicated. If you sit down and work it out carefully, you end up with Joda-Time (more or less - not in all the details, but in the set of abstractions). If you balk at that and make something simpler, you make a subtly but fundamentally broken API. It took a long time for us to get comfortable with the level of complexity in Joda-Time, but now nobody thinks a serious date/time API can be substantially simpler. It sounds to me like you and the author are saying that Go does this balking systematically.
- alexhutcheson 7y agoThe author of Joda-Time actually thinks that even Joda-Time didn't get it quite right, and believes the java.time libraries in Java 8 and above (aka JSR-310[1]) are better than Joda-Time: https://blog.joda.org/2009/11/why-jsr-310-isn-joda-time_4941.html https://blog.joda.org/2009/11/why-jsr-310-isn-joda-time_4941... It turns out that abstractions for time are really hard to get right. [1] https://jcp.org/en/jsr/detail?id=310 https://jcp.org/en/jsr/detail?id=310
- WalterBright 7y agoDate & Time need to be baked into the operating system so it only has to be gotten right once, and then every programming system benefits.
- dralley 7y agoSo long as they actually get it "right". Compare to Windows' APIs originally taking UCS-2, then UTF-16, when now we would all rather be using UTF-8.
- ben509 7y agoIn fairness to Windows and Java, they weren't wrong. There was no UTF-16, rather, UCS-2 was the accepted standard because the plan for Unicode was to encompass languages in use, not emojis and historical langauges. That changed and we're stuck with that legacy.
- erik_seaberg 7y agoEven without emoji, mashing up Chinese, Japanese, and Korean to fit in 21k was never going to happen. It’s sort of like asking Danes to stop spelling their names correctly because we can't afford the extra codepoint for “å”. https://en.wikipedia.org/wiki/Han_unification#Rationale_and_controversy https://en.wikipedia.org/wiki/Han_unification#Rationale_and_...
- jerf 7y agoThe problem is... and a lot of people aren't going to like this, including the original author... a lot of those particular gotchas are there for a reason. The author overestimates how much of them are intrinsic to the language, in my opinion. This is a cross-platform file interface, and we've had those for years. What we tend to discover is that if you do write something precisely correct on each platform, you lose a lot of value in the cross-platform bit. Technically, pretty much the entire interface ought to be slightly different between all OSes. All of them have relevant differences all the way around in permissions, behaviors, "inodes" or whatever the equivalent is or possibly no equivalent, whether they have "symlinks" or "hard links" or other bizarre things, whether a file is a single stream or multiple, the list goes on and on. It would be completely feasible to write the "os" package to intimately bind to each and every difference; as mentioned in the article, the cross-platform functionality is there. (Well... at least down to the OS level. Considering the full space of filesystems themselves get even more fun.) The consequence would be the near complete loss of ability to write cross-platform code beyond very trivial stuff. On its own, this is neither good nor bad. It is a matter of what the goal is, and the goal here was an 80/20 cross-platform functional library, as is the goal of most of the standard library. If you need the other 20, you need to do something other than the standard library, and that's the case for the entire library, not just "os". If you magically materialized this perfectly-matched cross-platform library and submitted it to the project to replace os, and even if we ignore the backwards-compatibility promises for Go 1, I virtually guarantee it would have been rejected even so. It's not the kind of library that Go wants as its standard library. It's a perfectly sensible sort of library. It just isn't what is desired in the standard library. "What is desirable in the standard library" is a very exclusive list. All cross-platform file interfaces are quirky if you really zoom in on them, because if you sit down and really play with it, like, beyond what a ranty single blog post would constitute, you'll find you can't get the quirks out. There is a essential level of quirkiness in the problem itself. I also would disagree that "stronger types" would solve the problem. You can easily write a Rust library that is basically the same thing as Go, even if it happened to have a slightly different set of quirks, using similar types across all platforms. You can easily get OS-targeted libraries that don't implement a virtual lowest-common denominator, but that means you get non-cross-platform code, on the grounds that it does not matter what the underlying language does, you can't access extended attributes on filesystems that don't have them, and if your concrete type forces you to deal with that on an OS-by-OS level, you can't share concrete types. "Rust could use traits to solve this!" In which case, the traits will themselves define a lowest-common denominator cross-platform library, with a more "accurate" library underneath. Go could use interfaces in essentially the same way, with the same consequence. You can't get away from the fact a LCD library will be quirky; it is only a matter of choosing the quirks you have, not whether you have them.
- seamyb88 7y agoHa! I only read the windows fs stuff and came here to see if the rest was worth reading.
- SlowRobotAhead 7y ago> at which point many of the readers got bored I don’t know Go or Rust, and yes, I did almost get bored and quit the article. However... glad I powered through because the 169 dependency packages that ended up bringing in GRPC and this Protobuffers and the kitchen sink was worth the read. In a 0.00% acceptable conclusion. The language shouldn’t encourage this imo. I get the idea. But I’m coming from embedded so when I see they have a 32bit argument with 4.29 billion options to represent a boolean, well, no sir I can’t get behind that at all :) Likewise I think the author would have been well to leave Rust out of it most of the time. Your gripes with x should be independent of “y lang does it better” aside from just knowing it is possible to be better.
- bluejekyll 7y ago> the author would have been well to leave Rust out of it most of the time. Initially I thought the same, but then I realized that it was being used as a means of expressing that it doesn’t have to be this way. That there are better choices, and here’s an example of better choices. Probably too much detail on the Rust, but using it to contrast with some of the poor decisions pointed out in Go is useful.
- nonbirithm 7y agoIs there a language besides Rust that could be used instead as this example? As in, languages whose standard library was so carefully designed from previous experience that the design of features like Permissions/PermissionsExt and OsString deliberately take into account the design of both Windows and Unix-like internals. The author mentioned part of the reason the filesystem API is so awkward in Go is because Go was designed from the start with Unix paradigms and Windows was not even considered because there was no reason to at the time - it started as a language internal to Google and their development priorities likely excluded Windows. When it became public and more widely used Windows support became a necessity for cross platform support, but there was no going back to redesign everything so it had to be bolted on within the paradigms the stdlib's interface allowed for. When it comes to the stdlib I feel you have to be extremely careful about these things and plan for Windows users ahead of time if you intend for cross platform support - and that should be a goal if it's to be widely used. I feel like Rust only accomplished what they did because they made sure to include Windows as a first-class platform in their philosophy from the start.
- _bxg1 7y agoYes, the latter half made much better and broader points
- hota_mazi 7y ago> the majority of which relate to Go's tendency to just be silently completely wrong 100% right on. Go's handling of errors is often ridiculed for its verbosity and lack of thought, but the fact that Go makes it so easy to sweep errors under the rug has real and devastating consequences in the real world. Go programs are much less safe than programs written in Rust or Java for that reason.
- rhinoceraptor 7y agoIt's not surprising that other pieces of golang have incredibly broken assumptions, I know this particular one was discovered independently by many people: https://github.com/golang/go/blob/71ab9fa312f8266379dbb358b9ee9303cde7bd6b/src/runtime/sys_linux_amd64.s#L139-L143 https://github.com/golang/go/blob/71ab9fa312f8266379dbb358b9... https://marcan.st/2017/12/debugging-an-evil-go-runtime-bug/ https://marcan.st/2017/12/debugging-an-evil-go-runtime-bug/
- aeontech 7y agoThat debug report was incredible, I have to say. Hats off to the author for their patience.
- rob74 7y agoI don't think this example justifies the expression "incredibly broken assumptions", I would rather call it an "incredible corner case"...
- kevingadd 7y agoMaking assumptions about the stack frame size of third-party code is incredibly broken. You have no way to make any guarantees about it and primitives like alloca mean you can't even be sure the stack size will be the same across multiple calls.
- amscanne 7y agoThe VDSO isn’t really any third-party code. It’s very surprising for it to do a stack probe. Though obviously it’s possible, and falls on the runtime to handle (so it’s a legit bug). But.. nearly every “runtime” makes stack assumptions. They usually just have enough slack that it doesn’t matter. E.g. musl has an 80k stack by default — is it still “incredibly broken” if the VDSO needs > 80k? No. While the VDSO doesn’t have an explicit stack requirement, it’s definitely implicit that it will use a small, reasonable amount.
- klodolph 7y agoThat said… I feel that Rust’s use of WTF-8 for OsString on Windows has resulted in some really nasty problems, especially since OsString doesn’t expose any useful methods for string manipulation. As far as I can tell, Rust’s approach fails to hide any of the complexity, and then adds the additional complexity of a new encoding and conversions on top. I can see that there’s some end goal of being able to work with OsString in Rust code but at the moment the API is missing everything except a couple functions to convert it into something else. It’s a truly cursed problem that we have three separate notions of strings. We have Unicode strings, we have bytestrings, and we have wchar_t strings on Windows. No two of these are completely interoperable. This has a ton of direct consequences which cannot be completely avoided. For example, if I want to make a version of “ls” that gives a result in JSON, I’m already fucked and I have to change my requirements.
- steveklabnik 7y agoI have felt this pain for sure, but only really once. This is because, in languages with strings as paths I tend to use string manipulation to do operations on paths, but given that virtually all of my OsString usage is paths, which have specific manipulation functions already it’s lesser. This is also why the interface isn’t so rich, there just hasn’t been a lot of demand. That said I think some things are in the pipeline?
- ChrisSD 7y agoDo you have any more info about what's in the pipeline?
- steveklabnik 7y agoI thought that someone had recently sent in a PR adding some convenience functions, but I can't find them now, I must be imagining things :(
- edflsafoiewq 7y agohttps://github.com/rust-lang/rust/issues/49802 https://github.com/rust-lang/rust/issues/49802
- btilly 7y agoAnd the other one is the tendency for Go's design to say "exceptions are allowed for me but not for thee". When I worked at Google I used other languages by some of the same authors and they showed the same design philosophy. Make things with an enforced simplicity, and where there were more special use cases that the language designer needed, they created escape hatches for themselves but not the language users.
- Animats 7y agoAnd the other one is the tendency for Go's design to say "exceptions are allowed for me but not for thee". Yes. Exceptions are kind of a pain, but the workarounds for not having them are worse. Passing back "result" types tends to lose the details of the problem before they are handled. Rust is on, what, their third error handling framework? Exceptions have a bad reputation because C++ and Java botched them. You need an exception hierarchy, where you can catch exception types near the tree root and get all the children of that exception type. Otherwise, knowing exactly what exceptions can be raised in the stack becomes a huge headache. Python comes close to getting this right. Incidentally, the "with" clause in Python is one of the few constructs which can unwind a nested exception properly. Resource Acquisition Is Initialization is fine; it's Resource Deletion Is Cleanup that has problems. Raising an exception in a destructor is not happy-making. It's easier in garbage-collected languages, which, of course, Go is. You have to be more careful about unwinding in non garbage collected languages, which was the usual problem in C++.
- AnimalMuppet 7y ago> Exceptions have a bad reputation because C++ and Java botched them. Okay... > You need an exception hierarchy, where you can catch exception types near the tree root and get all the children of that exception type. Didn't Java do exactly that?
- Animats 7y agoThey tried, but the hierarchy is not well-designed. You want a clear distinction between "program has an internal problem" and "external thing (network, file, database, remote service, etc.) had a problem". You usually want to catch "external thing had a problem" in whatever wanted to talk to the external thing. "Program has an internal problem" usually requires restarting the program.
- tschellenbach 7y agoOn the other hand, why care about Windows?
- yashap 7y agoAgreed, the author’s main argument is summarized nicely at the end, and it’s a good one: > It constantly takes power away from its users, reserving it for itself. > It constantly lies about how complicated real-world systems are, and optimize for the 90% case, ignoring correctness. > It is a minefield of subtle gotchas that have very real implications - everything looks simple on the surface, but nothing is. “Our users are stupid, prefer subtly wrong to a more complex but correct abstraction” is core to Go, and appears everywhere.
- taylodl 7y agoThat's indicative of a serious attitude problem - I am so smart and can handle the power but you can't. Contrast this with C where everybody is on equal footing. Thanks for posting. This tells me everything I need to know to not get on Mr. Golang's Wild Ride.
- bshacklett 7y agoUnfortunately that's becoming increasingly difficult with the amount of programs written in go that touch developers lives on a regular basis. Kubernetes, for example, is written in go. Want to fix a bug? You're now at least a part-time Gopher. I don't think we've seen the likes of this since PHP.
- taylodl 7y agoKubernetes is always brought up when discussing Go. I wonder if Go might ultimately be its Achilles' heel? If so, I wonder how that might manifest itself?
- FridgeSeal 7y agoI cannot assert to the full correctness, and I’m aware of the heresay-ness of it, but I’ve seen discussion before on how much “hacking around the lack of generics is in the Kubernetes codebase”
- remus 7y ago
- bdnro9nn 7y agoIt’s a pretty recent language to the production ecosystem. Is it any different than Java or Python 20 years ago? How much of this is “wrong” given the relativeness of wrong when it comes to what is effectively how to organize a syntax construct hierarchy? You can find the same ranting all over about C, Python, etc Oh look computer people got an opinion on the organization of computer stuff. Shock, awe
- int_19h 7y agoIt's not immediately obvious, but the rant is less about the specifics of the design, and more about the assumptions and attitudes underpinning said design - and how they translate to bad design all around, not just those particular pain points.
- quantified 7y agoNot disagreeing. While some readers got bored (understood), others got more interested at the detail to which this was being taken and enjoying the care of discussion. I’ve never programmed in Go from a vague sense of these issues. Hey, it confirms my uninformed biases!