Live data from Hacker News

I Want Off Mr. Golang's Wild Ride

fasterthanli.me

371–380 of 508 posts

Re: I Want Off Mr. Golang's Wild Ride

#371
post #137

Earlier quoted context omitted.

fn main() { let x = 1; let x = "foo"; } Does this make rust not strongly typed? Here's a Rust program with no types in the source code. It seems the issue you're objecting to is that python doesn't differentiate variable declaration from assignment (The fact we need let twice in this code is a result of Rust doing this). Which is a fair thing to complain about (and why Python had the "nonlocal" and "global" keywords)…

That's not an identical translation, the identical Rust would be fn main() { let mut x = 1; x = "foo"; } which indeed fails to compile with a type error. (That being said there is a conflation of static/dynamic and weak/strong going on in this thread, as there always is in these kinds of discussions.)

I was incorrect here! I don't know Python as well as I thought. Thanks for all the discusison folks.

Re: I Want Off Mr. Golang's Wild Ride

#372
post #237

Earlier quoted context omitted.

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…

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…

Strings are the same story, except there's still a widespread (and incorrect) perception that they're simple - probably because it's a built-in type in almost every language.

Re: I Want Off Mr. Golang's Wild Ride

#373
post #362

Earlier quoted context omitted.

No, because anyone can use `unsafe` in Rust. GP’s point was that Go can use the feature internally but no end-user can.

That sounds more like a legal argument than an engineering one. I mean, who cares what the mechanisms are and what the precise rules and enforcement mechanisms are? The point is there are complicated features that are OK in some contexts but not others, that this varies between problems and between languages, and that some parties make different decisions on how to make use of them even when implementing software for…

It is very much an engineering argument, because writing maintainable software requires respecting contracts. If an escape hatch is private, you can't depend on it, because code doing so will break when the hatch changes (and there's no guarantee that it won't).

Re: I Want Off Mr. Golang's Wild Ride

#374
post #22

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.

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

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.

Re: I Want Off Mr. Golang's Wild Ride

#375

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.…

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.

Re: I Want Off Mr. Golang's Wild Ride

#376

Earlier quoted context omitted.

The 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... It turns out that abstractions for time are really hard to get right. [1] https://jcp.org/en/jsr/detail?id=310

Date & Time need to be baked into the operating system so it only has to be gotten right once, and then every programming system benefits.

How much does that actually help? Some things are much easier when system calls return sane values, but the standard library of a programming language needs to work as best it can on many platforms.

Re: I Want Off Mr. Golang's Wild Ride

#377

Earlier quoted context omitted.

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 OsS…

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.

In that case... You wouldn't be using the rust file system libraries though, right?

It seems like the simplest definition of an OsString is "the type used to interact with the OS file system API as implemented in rust".

Re: I Want Off Mr. Golang's Wild Ride

#378
post #306

Earlier quoted context omitted.

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 use…

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.

Unfortunately 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.

Re: I Want Off Mr. Golang's Wild Ride

#379
post #319

Earlier quoted context omitted.

That's an issue of scoping, not capturing. The x in the lambda isn't scoped to the lambda, it's scoped to the surrounding environment. So the x closes not over the lambda but the outer scope. So it's as expected given shadowing. Edit: Since I'm getting throttled: No, I'm saying that scoping rules are different in python and rust. In Rust (and cpp) there's the concept of scopes/closures as a first class feature. This…

Setting aside any terminology for a second, consider this rust program: fn main() { let x = 1; let capture = || x; let x = 2; println!("{}", capture()); println!("{}", x) } This will print 1 and then 2, whereas python would print 2 and 2. Hence, you can see that the formulation "let mut" is equivalent to python, not "let" followed by "let". Here's the rust program that prints 2 and 2: fn main() { let mut x = 1; let p…

Actually hmm, I may want to take back my earlier comment. There are multiple things at play. There's scoping (where rust will copy across scope boundaries for non-ref types, which allows closing over something as in your first example above).

Then there's mutable refs and mutable variables, which as hope-striker mentioned I was confusing, possibly because I was using ints in my example. If instead we used a vec:

    fn main() {
     let x = vec![0,1,2]
     x.push(3) // fails since x isn't mutable
    }
There's no clear direct related concept here by default. If we're allowed to use pytype, you get this:

    def main():
      x: Sequence[int] = [1,2,3]  # Sequences aren't mutable
      x.push(3)  # fails since x isn't mutable
Cool, so mutable and immutable values are possible in both langs. What about refs? Well we went through that one, if you pass a mutable ref to a function in rust, you can modify the ref in ways that just aren't possible in python:

    fn main() {
        println!("Hello, world!");
        let mut x: i32 = 3;
        modifies(&mut x);
        println!("{}", x);
    }

    fn modifies(x: &mut i32) {
        *x = 5;
    }
There's nothing analogous to this in python. Everything is always passed as a mutable "value"[1], nothing is passed as a ref.

Cool so that's mutable variables and mutable references. That leaves this weird scoping issue. In rust (and in cpp) there's lots of scopes. Any set of braces creates a new scope, and so shadowing can happen across scopes. Lambda capture/closure happens over the scope. A given scope binds a name to a value, or a set of names to their values.

Python's a bit different, only new names are created in the scope. If a name isn't accessible in the given scope, the name is pulled from parent scopes etc.

So for the capturing behavior you want, there's weird nonlocal stuff that needs to be done, or you can explicitly make an additional scope, which removes the wonky behavior. If the name were really mutable, you'd be able to change what x referred to in the enclosing scope, which you can't.

tl;dr: This isn't mutable names, its python's (admittedly abnormal) scoping rules.

[1]: Unless you add in mypy or whatnot, where the typechecker will prevent you from modifying something that is non-mutable, but unlike in rust this isn't done with mutability as a first class citizen, its just that some interfaces expose mutating methods (`append`) and some don't. You can pass a list to a function that expects a list or a sequence, and the first case is mutable, while the second isn't.

Re: I Want Off Mr. Golang's Wild Ride

#380

Earlier quoted context omitted.

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.

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…

> Is there a database library that uses reflection that properly descends into type aliases?

The database package uses a type assertions to find the methods, not reflection.

Go types have one level of underlying type, not multiple levels as you seem to be assuming. Go is simplistic compared to other languages in this regard.

A type definition defines a new type using the underlying type of some other type.

I can understand the complaint that Go does not have the aliasing feature that you want, but the database/sql and encoding/json packages work exactly as expected given Go's simple model.

Post reply on HN