Readit News logoReadit News
fhood · 6 years 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 · 6 years ago
This.

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 · 6 years ago
A 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.

btilly · 6 years ago
And 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 · 6 years ago
And 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++.

agumonkey · 6 years ago
> , they created escape hatches for themselves but not the language users.

man, seriously disappointing..

ajross · 6 years ago
Is that wrong, though? I mean, confounding features like exceptions are... useful. That's why they found in languages in the first place. They're just footguns when used "wrongly". So why not have an "escape" hatch for experts that isn't part of the standard/official/blessed paradigm of the language?

I mean, isn't this exactly what Rust did with unsafe? Warn everyone away from it, promise that "normal" code will never need it, but... include it in the language and use it pervasively where needed in the inner workings of the runtime?

yashap · 6 years ago
Agreed, 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 · 6 years ago
That'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.
Corrado · 6 years ago
I like to think of it more like Go was built by Google, for Google. If you aren't Google, then Go is probably not right for you.
hota_mazi · 6 years 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 · 6 years ago
It'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/71ab9fa312f8266379dbb358b9...

https://marcan.st/2017/12/debugging-an-evil-go-runtime-bug/

whatsmyusername · 6 years ago
Java just hides them in 40kb of logs that don't tell me anything.

I agree that golang makes it easy to lose errors but using Java as a better comparison is laughable in practical use.

Thaxll · 6 years ago
"Go programs are much less safe than programs written in Rust or Java for that reason."

This is plain wrong. ( Rust included )

klodolph · 6 years ago
That 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.

Fellshard · 6 years ago
Isn't the point of OsString that it's essentially a faux-union type that is intended to be immediately converted to some concrete representation which /is/ richly manipulable?
steveklabnik · 6 years ago
I 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?

buckminster · 6 years ago
Rust still doesn't get this right. If I'm calling an NFS library, say, on Windows I need to use UNIX paths. Rust needs WindowsString and UnixString on every platform, with OsString as a synonym for whichever is most useful locally.
michael_j_ward · 6 years ago
Doesn't address the OsString complexity- but the author has another post on strings in Rust that you might be interested in [1]. It at least addresses how Rust does hide a lot of regular `String` complexity that `C` doesn't.

[1] https://fasterthanli.me/blog/2020/working-with-strings-in-ru...

heelix · 6 years ago
Not a Rust guy, but I did have to look up WTF-8. Had to chuckle at the 'wobbly transformation format' as I was translating it something else. :)
SlowRobotAhead · 6 years 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 · 6 years 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.

irishsultan · 6 years ago
> Your gripes with x should be independent of “y lang does it better” aside from just knowing it is possible to be better.

And what better way to demonstrate it's possible to do better by having an example ready?

jerf · 6 years ago
The 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.

fasterthanlime · 6 years ago
I share your conclusion that "the perfect cross-platform library" does not exist, and I also agree that we could use Rust to make one that's worse than the Rust standard library, and we could use Go to make one that's better than the Go standard library.

However, Go's limitations make it hard to make one that is much better than the Go standard library. And the Rust standard library is so carefully designed, that there's really no need to go ahead and redo the work. Unless you need something it explicitly doesn't support (and doesn't promise to support!), in which case there's a wealth of crates at your disposal, which also benefit from a rich type system and strict compiler checks to ensure correctness at compile-time.

People don't just switch to Rust and write code like they did before. It's different enough that it makes everyone rethink how they approach a problem. But it doesn't just get in your way - not only are the compile errors excellent (and the core team is working tirelessly to improve them even further), it gives you the tools to build solutions you'd never pull off in other systems languages.

jayd16 · 6 years ago
This is a lot of words about how a cross platform library is good. The author never said otherwise.

The complaints are about how the libraries as implemented are subtly wrong and use simplicity as an excuse.

ativzzz · 6 years ago
> 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.

This is similar to the pros/cons for using a mobile framework like React Native, where you get a lot of productivity in cross platform design but you lose some of the precision compared to developing for each platform natively.

If you need that precision, then you need to use something native. It is simply a tradeoff of business needs. Do you need cross platform productivity or per-platform precision? If you need precision, then reconsider your engineering tools.

scythe · 6 years ago
>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.

This sounds like a reasonable argument if your language is, say, Julia, or something like Lua, where in the first case you probably don't write code that needs to do a lot of work at the OS/network/hardware level, i.e. systems programming, and in the second case, the language has a good built in FFI that lets you drop down into C or a similar language to do systems programming. Python, Clojure and Ruby fit more-or-less into the second case. C simply forces you to do all of the work yourself.

But Go's FFI (cgo) is a constant source of consternation, and while Go's authors admit it might not be suitable for "the largest" codebases, the hostility of Go to good FFI makes it more uncomfortable to use in practice. The official viewpoint is something to the effect of "most people shouldn't need cgo". The result has been a proliferation of libraries that attempt to do systems programming in Go, which includes in particular the "monotime" debacle highlighted by the author.

Remember: this blog post highlighted this weirdness in a real library used to solve a real problem. The idea that Go isn't meant to be used for that is belied by the fact that many, many people do try to use Go for that.

So yes, if Go had a more "difficult" file library, it would be less consistent with the "simplicity" idea used to advertise the language, but it might be more consistent with the way that Go is used in practice.

fhood · 6 years ago
I don't think the windows vs everything else is the main point of this article. I think it is just an example of a design trend, and even within that section of the article the author mentions unix specific design flaws of go file handling.
_bxg1 · 6 years ago
Yes, the latter half made much better and broader points
quantified · 6 years ago
Not 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!

Dead Comment

tschellenbach · 6 years ago
On the other hand, why care about Windows?
bdnro9nn · 6 years ago
It’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 · 6 years ago
It'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.
whateveracct · 6 years ago
Here's an example of why Go's simplicity is complicated:

Say I want to take a uuid.UUID [1] and use it as my id type for some database structs.

At first, I just use naked UUIDs as the struct field types, but as my project grows, I find that it would be nice to give them all unique types to both avoid mixups and to make all my query functions clearer as to which id they are using.

    type DogId uuid.UUID
    type CatId uuid.UUID
I go to run my tests (thank goodness I have tests for my queries) and everything breaks! Postgres is complaining that I'm trying to use bytes as a UUID. What gives? When I remove the type definition and use naked UUIDs, it works fine!

The issue is Go encourages reflection for this use-case. The Scan() and Value() methods of a type tell the sql driver how to (de)serialize the type. uuid.UUID has those methods, but when I use a type definition around UUID, it loses those methods.

So the correct way to wrap a UUID to use in your DB is this:

    type DogId struct { uuid.UUID }
    type CatId struct { uuid.UUID }
Go promised me that I wouldn't have to deal with such weird specific knowledge of its semantics. But alas I always do.

[1] https://github.com/google/uuid

EDIT: This issue also affects encoding/json. You can see it in this playground for yourself! https://play.golang.org/p/erfcSIe-Z7b

EDIT: I wrongly used type aliases in the original example, but my issue is with type definitions (`type X Y` instead of `type X = Y`). So all you commenters saying that I did the wrong thing, have another look!

hajile · 6 years ago
Is there even a single go abstraction that doesn't leak it's guts everywhere?
thedance · 6 years ago
There aren't any abstractions in any language or library that don't leak everything about what they are trying to hide as well as everything about their own implementation. That's just life. It's impossible to hide complexity. Whatever wraps one thing will be strictly more complex than the wrapped thing was.
uhoh-itsmaciek · 6 years ago
Not to mention that sql.Result (the return value of Exec) has a LastInsertId() that's an int64, so if you're using uuids, you can't use that at all and have to call Query instead and manage generated IDs yourself.
HelloNurse · 6 years ago
This is a more ridiculous symptom of bad library design than the filesystem trouble mentioned by the article.

In the real world, executing most SQL statement could be made to return a semi-useful integer according to simple and consistent rules (e.g. affected row count, -1 if there's no meaningful integer).

But the official Go documentation

https://golang.org/pkg/database/sql/#Result

makes it quite clear that the Go design committee decided to imitate a remarkably limited and inelegant MySQL function that returns the value of an auto-increment column, not even realizing that only a few statements have auto-increment columns to begin with. I'd call this a negative amount of design effort.

  LastInsertId returns the integer generated by the database
  in response to a command. Typically this will be from an
  "auto increment" column when inserting a new row. Not all
  databases support this feature, and the syntax of such
  statements varies.
(Of course, MySQL's LAST_INSERT_ID() is only bad as a building block and inspiration for a general API; in SQL queries assumptions aren't a problem and overspecialized tools can be occasionally very useful)

Cthulhu_ · 6 years ago
In a lot of cases - esp. distributed systems - it's not up to a database to generate a UUID, but the application. In theory you can have a hundred servers that generate records and send them to a central storage platform (which may or may not be a database, or event bus, etc).

UUIDs are not meant to be generated by databases.

whateveracct · 6 years ago
Haha yes I've long since resigned myself to using Query + explicit RETURNING for inserts

Cut 2 of 1000

throwaway894345 · 6 years ago
I agree that the standard library database tooling is really clumsy in a lot of cases, but it's the library implementation at fault, not Go itself. Notably, contrary to your last sentence, you aren't troubling yourself with "weird Go semantics", you're troubling yourself with the semantics of the database stdlib.
whateveracct · 6 years ago
Is there a database library that uses reflection that properly descends into type aliases? Probably not, because it isn't always what you want.

It's still fundamentally caused by Go's shitty design choices.

encoding/json is at fault as well, which is also in the stdlib and a flagship library (basically part of the language - the maintainers wouldn't even extend its struct tag parsing to allow for required fields it's so fundamental!)

Proof that this issue affects JSON: https://play.golang.org/p/erfcSIe-Z7b

skrtskrt · 6 years ago
I'm not sure, I kind of like this?

Personally I would never create different UUID types for each DB struct, and have never seen that done.

    type DogId struct { uuid.UUID }
is type composition (Sorry if I'm not using the right term there). Isn't this exactly how you're supposed to do what you're trying to do in Go?

AnimalMuppet · 6 years ago
> Go promised me that I wouldn't have to deal with such weird specific knowledge of its semantics.

Where, specifically, did Go promise you that? I know of no languages where you don't, sooner or later, have to have weird specific knowledge of the semantics.

whateveracct · 6 years ago
In all of its marketing and propaganda? Constant comparisons to languages like Java bemoaning boilerplate and idiosyncrasies?

I think it's perfectly fair to say that this behavior is not in line with Go's philosophy.

anonymoushn · 6 years ago
One company asked me to write a wrapper around database/sql that adds a connection pool. This is pretty easy until you want to implement any function that returns Row, which you just can't, because you can't make one of those. Amazing.
jimsmart · 6 years ago
Take care, you may be wrapping your connection pool around a built-in connection pool. Furthermore, some dbs might not like log-lived connections.

http://go-database-sql.org/connection-pool.html

BarkMore · 6 years ago
A type alias binds a new name to a type. All of the type’s methods are available through the new name.
whateveracct · 6 years ago
I fixed my comment. I didn't mean to have equal signs in there.

Proof that `type X Y` causes the issue: https://play.golang.org/p/erfcSIe-Z7b

sagichmal · 6 years ago
Type aliases (type X = Y) are a niche feature and not meant for this use case.

If you had done e.g. type X Y you would have had more success.

masklinn · 6 years ago
I think the OP simply wrote them the wrong way around, because `type X = Y` would not have broken anything as it'd make X a trivial alias to Y, and thus would not be losing anything. Even the reflected name doesn't change from the original.

type X Y however does create an entirely new type which is physically identical to the original but logically unrelated (without any of the methods or anything).

Which is fine in the sense that it's what OP was looking for (completely independent types) but less so in that it doesn't forward any method, and it's not necessarily clear how you'd do that (type conversion, which looks really shitty when it involves pointers)

whateveracct · 6 years ago
Sorry I miswrote. I meant `type X Y` by "type alias" (terminology from other languages)

`type X Y` is still affected - you will 100% _not_ have more success :)

Proof (using JSON this time): https://play.golang.org/p/erfcSIe-Z7b

apta · 6 years ago
golang's broken interface design also encourages hacks (which lead to difficult to track down bugs) like this: https://github.com/golang/go/issues/16474
dilap · 6 years ago
If you imagine a spectrum of languages from sloppy-but-"easy" to precise-but-"hard", with something like Python or Ruby way off on the left and something like Rust way off on the right, Go is sitting somewhere in the middle.

And so if what you're craving is absolute precision and maximal avoidance of errors or incorrect behavior, then Go is not going to be your jam. I sympathize w/ that.

That said, these specific complaints don't strike me as that bad.

- Filesystem perms exposed on windows, which just no-op. This seems pretty reasonable, though!

- Filesystem paths represented as str type, which is assumed to be utf8, but doesn't have to be. This also seems reasonable! If you want to check for invalid utf8 and specifically print out something special in that case, nothing in Go is stopping you from doing that. This is a classic "easy but sloppy" vs "hard but precise" tradeoff.

- Timeout thing -- I'm a little confused here, or maybe not up-to-date. He says let's do things the "modern" way and pass a context to do HTTP timeouts, which apparently doesn't work, and then goes off on a 3rd party package to then fix this which has an insane dependency graph. But...if you just set the Timeout field on the http client, everything works correctly. So what's the problem? Or am I missing something?

hinkley · 6 years ago
Often these choices are 2 dimensional, but people don't see it because we can always agree that there's one quadrant that nobody wants, but usually there's another quadrant that some people really want, and others need but don't think they want. It gets ignored and everyone behaves as if X = Y when in fact it should be X <= Y

As a concrete example, I was struck by something in an interview where the consultant pointed out that easy to implement functionality gets copied by your competitors quickly. Differentiating features are ones that are very valuable but tricky to get right. But nobody wants to prioritize those and so (my words) whole industries are boring dystopias of cheap features with no kick. You should want to implement some features that are worth far more than the trouble of implementing them, regardless of how much trouble it is.

Similarly, getting a concise design may be one of the hardest things we can do. So we end up with naive or baroque most of the time. When someone stumbles onto something better a bunch of us copy them in the next generation of tools, but the inspiration/perspiration balance is very evident in the slow rate of change we see.

fhood · 6 years ago
The http client isn't always directly exposed. I agree with the author. Context is a per request object and timeout should be able to function on a per request basis. Client is often shared and reused, and thus not always exposed in certain design patterns. If context has a timeout why doesn't it work as you would expect?

Also, now that I think about it, why does the basic http.get call mentioned in every go networking tutorial not have a default timeout?

dilap · 6 years ago
(I have never personally used context, so I'm not so sure what the expectations are with that.)

Looking at the http docs, I don't see any reason to believe setting a context for a request would control timeouts.

If the complaint is, "the http library API does not provide a way to set timeouts on a per-request basis," then OK, I guess, that's true, but I don't see why that should be a huge issue (just use different clients for the different timeout values you need).

But if you really don't want to do that, it should be easy enough to access the underlying network connection and set the timeout before reading the body, though I've never done this.

What Go is doing here still seems very reasonable from my perspective...

dom96 · 6 years ago
If Go is somewhere in the middle and Rust is on the right then Nim is somewhere in between Go and Rust.

Personally I think that’s the sweet spot, and now that Nim is at 1.0 there is no excuse not to give it a try. As nice as Rust’s compile-time memory management is, it’s very often overkill.

totalperspectiv · 6 years ago
> And so if what you're craving is absolute precision and maximal avoidance of errors or incorrect behavior

If you are being paid to develop software, doing anything other than aiming for absolute correctness seems negligent, at best.

I think this is part of what leads to obsession with Rust. We build so many things on a daily basis with a long long list of 'it depends'. But Rust aims to make you write something as correctly as possible, and provides a really solid base for you to do this. So that list of 'it depends' shrinks drastically and you feel superhuman for building something so solid.

ClumsyPilot · 6 years ago
You statement about absolute correctness doe snot really make sence -vast majorulity of bugs in all software I've ever used are not due to the language design, but are due to blatant mistakes of the application developers.
objektif · 6 years ago
You dont have to call Python sloppy just because Go sucks at some things.
jsjddbbwj · 6 years ago
Weakly typed is sloppy.
_bxg1 · 6 years ago
I think the mistake may be assuming that Go is meant to be a general-purpose language. From what I can tell, it's purpose-built to be a "web services" language, and its design-decisions center around that. What does that mean?

- It's expected to be run on Linux servers (not Windows) and developer workstations (probably not Windows).

- It needs to be fast but not blisteringly fast. Micro-performance concerns like the Time object thing are devalued.

- Embedded use-cases are probably not given too much attention.

- Agility in working with dynamic data (because that data is often foreign) is valued over flawlessly safe types.

By deciding not to worry about certain use-cases, the language can be more developer-efficient for its intended use-cases. In this light, for better or worse, I think the decisions made make a lot more sense.

jjnoakes · 6 years ago
If the language was meant to be run on unix machines for web services, it shouldn't support anything else in a half-done manner with silent errors and corruption.

The situation as it is now is just poor language and/or library design - choosing to support different operating systems with an API that requires the wrong thing to happen in some cases.

_bxg1 · 6 years ago
If you workstation does happen to be Windows, support-with-edge-cases may be plenty good enough for you to get your work done and if you run into a problem on your dev machine, it's much less of a big deal
zug_zug · 6 years ago
Except docker is written in go. Guess they never got the memo to not use go for non-webservices...
_bxg1 · 6 years ago
Fair, although it has very similar constraints/goals to those listed above
lwb · 6 years ago
Docker’s primary use case is also web services.
Liru · 6 years ago
My most popular project on Github is currently a program I slapped together in Go a long time ago. The `sync/atomic` issue mentioned at the end of the article is THE issue that made me stop considering Go for anything other than trivial things. Lack of decent error handling, a terrible builtin json library, constant `interface{}` to poorly substitute for generics, the package management issues that made Node.js look well-thought-out by comparison, struct field tags, and generators provided by the core team that set off linters provided by the core team with no good way to silence them kind of piled on before that, but the `atomic` issue is the one that made me avoid it. The author is right, all the little things add up.

Note that a bunch of these may have been fixed since I last used it, but honestly, I haven't checked because it was frustrating working in it and debugging it. It's a shame, `pprof` and the race detector are pretty cool.

gavinray · 6 years ago
Holy shit, the entire explanation of the absurd reasoning behind needing to use the getlantern/idletiming lib, and the debacle behind unraveling it's dependencies is pure gold. When the breadcrumb trail to dependency-hell stems from a file whose contents are:

    // This file is intentionally empty.
    // It's a workaround for https://github.com/golang/go/issues/15006
I about fell out of my chair. Pure gold.

typon · 6 years ago
I don't understand why you would create a statically typed language but not actually take advantage of types, instead typing everything with generic types like string. Why make the user pay for complexity in types but not actually deliver their promise? This is the problem with C and Go doesn't really solve it either
hinkley · 6 years ago
We just ran a pretty high profile 20 year experiment with Stringly Typed languages - Java.

Generally we try to avoid the mistakes of the previous generation (and make the same ones as the one before that, half the time) so this is confusing to me.

I wonder how many Android contributors he had working with him while these decisions were being made.

terminaljunkid · 6 years ago
There is a sweet spot and for different people, that spot lies in different places.

Having a proliferation of types is bad for everyone but highest order FP Weenies among us.

madhadron · 6 years ago
> The Go way is to half-ass things.

This used to be known as the New Jersey school, and is the underlying philosophy of Unix: build a bunch of little pieces that work a lot of the time and kind of fit together if you remember the gotchas, then call it a day. There is an essay on this that I am unable to locate right now which mentions the horror of someone working on ITS when they asked how Unix solved a rollback on error case in a system call and were told, "Oh, we just leave it inconsistent, and the application programmer has to deal with it."

Does anyone else remember this citation? I truly am failing to find it this morning.

skrebbel · 6 years ago
Sounds like it might be straight from the original "worse is better" essay:

http://dreamsongs.com/RiseOfWorseIsBetter.html

> The MIT guy did not see any code that handled this case and asked the New Jersey guy how the problem was handled. The New Jersey guy said that the Unix folks were aware of the problem, but the solution was for the system routine to always finish, but sometimes an error code would be returned that signaled that the system routine had failed to complete its action. A correct user program, then, had to check the error code to determine whether to simply try the system routine again. The MIT guy did not like this solution because it was not the right thing.

wrs · 6 years ago
Ironically, that is referring to the EINTR error code that I predict is about to cause a bunch of unexpected failures when people switch to Go 1.14. [0]

[0] https://golang.org/doc/go1.14#runtime

madhadron · 6 years ago
Oh, of course. Thank you!
msla · 6 years ago
Worse Is Better is a form of prioritization, especially in the face of changing requirements. Right Thing assumes you not only have lots of time to polish every piece, but that the goal is a fixed point you've already decided upon, so none of your work is going to be wasted.
simscitizen · 6 years ago
staplung · 6 years ago
Aissen · 6 years ago
jwz is redirecting this link based on referer, you might want to not click on it (copy/paste works).