Live data from Hacker News

Go generics are not bad

lemire.me

291–300 of 305 posts

Re: Go generics are not bad

#291
post #260

Earlier quoted context omitted.

I do agree with that, and with the implication that one shouldn't add generics (or other abstraction) where they don't provide enough value for their costs. But I'm confused because your example (adding a generic parameter to a function) seems to be an example of adding abstraction to code that did not previously have enough abstraction.

Yeah for sure. If we need more abstraction we need it, generics are just really, really abstract. I'm just saying generics should be a last resort. And if you find yourself with generics all over the place, you might take a step back and ask, did I make a bad architectural decision that will blow up in my face later? Can I do a medium sized refactor now to save myself a massive refactor later? Coming from Python, I h…

I am now fairly sure we agree and I just didn't like your example.

Re: Go generics are not bad

#292

Earlier quoted context omitted.

> the most straightforward interpretation of that claim is that there is no None/Some(None) distinction, and this is straightforwardly not true. That’s not the most straightforward interpretation, since it would invalidate the original criticism that I responded to (that Go more-or-less uniquely requires you to check inner/outer nil). > For some reason, most languages do not encourage the users to create types that a…

A difference between Go and Java is that a null Regex in Java will not be implicitly converted to a non-null Stringable containing a null Regex. If you'd like, I can post about "implicit conversion from nil pointers to non-nil interfaces containing nil pointers that can only be checked for nil using reflection, combined with interfaces that work by duck typing and an existing body of code that pervasively uses struct…

> A difference between Go and Java is that a null Regex in Java will not be implicitly converted to a non-null Stringable containing a null Regex.

You have it exactly backwards--you're arguing that Go should implicitly convert a not-nil interface (that is implemented by a nil pointer) to a nil interface so that Go interfaces behave more like Java interfaces, but Go isn't doing any implicit conversions (unless you consider "assigning a concrete value to an interface" to be an "implicit conversion", but Java does this too).

> implicit conversion from nil pointers to non-nil interfaces containing nil pointers

Again, Go doesn't do any such implicit conversion, and you're arguing the position that it should. An interface is just a tuple containing a pointer to the data and a pointer to the type `(pdata, ptype)`. A nil interface is `(nil, nil)`, and an interface that is implemented by a nil pointer is `(nil, )`. You're arguing the position that the latter should be implicitly converted to the former, but this would break code that uses nil as a valid value.

The root cause of the problem isn't how Go handles nil pointers in interfaces, but rather that Go has nil pointers, interfaces, etc at all. Go gives you no way to express that a reference type can't be nil, but ideally it would have an enum system like Rust's that makes optionality an opt-in property via `Option`. If an interface `Bar` is implemented by `*Foo` and neither `*Foo` nor `Bar` can be nil, then this problem largely goes away. If `Bar` is implemented by `Option`, then `Option`'s methods need to handle both the `Some(*Foo)` and `None` (i.e., nil) cases.

> that can only be checked for nil using reflection

You don't need reflection:

    i, ok := (iface).(*int)
    if !ok {
        fmt.Println("`iface` is not an `*int`")
    }
    if i == nil {
        fmt.Println("`iface` contains a nil pointer")
    }
    fmt.Printf("`iface` contains a pointer to `%d`", i)**

Re: Go generics are not bad

#293
post #195

Earlier quoted context omitted.

While something may be well understood in an academic sense doesn't mean that the application of that understanding to implementation humans will take a liking to is well understood. If you look at the programming language landscape I think one wouldn't be unreasonable in pointing out that very little is understood in terms of how you go from good ideas to widely adopted language. What affects reality is important.

> doesn't mean that the application of that understanding to implementation humans will take a liking to is well understood. I really like how ML implements it, does that make me somehow less human?

What you like or don't like isn't really the point. The point is that it isn't unimportant when judging how "good" a language is to take into account how many people are willing to use it. It is hard to argue that a language is "good" when a negligible fraction of developers are willing to use it.

Re: Go generics are not bad

#294

Earlier quoted context omitted.

A difference between Go and Java is that a null Regex in Java will not be implicitly converted to a non-null Stringable containing a null Regex. If you'd like, I can post about "implicit conversion from nil pointers to non-nil interfaces containing nil pointers that can only be checked for nil using reflection, combined with interfaces that work by duck typing and an existing body of code that pervasively uses struct…

> A difference between Go and Java is that a null Regex in Java will not be implicitly converted to a non-null Stringable containing a null Regex. You have it exactly backwards--you're arguing that Go should implicitly convert a not-nil interface (that is implemented by a nil pointer) to a nil interface so that Go interfaces behave more like Java interfaces, but Go isn't doing any implicit conversions (unless you con…

> Go isn't doing any implicit conversions

What do you call it when you assign a value of one type to a variable of another type and the language creates a new value, different from the one you were assigning, and stores that new value in the variable? In C++ this is called "implicit conversion." Here's the tutorial again:

  var a Abser
  f := MyFloat(-math.Sqrt2)
  v := Vertex{3, 4}
  
  a = f  // a MyFloat implements Abser
  a = &v // a *Vertex implements Abser
The last two lines involve constructing a new value for the LHS of a type that differs from the type on the RHS without any function calls, constructors, "make" keywords, etc. What word would you like to use for this?

You could just as well have a patch that changes the type of a single variable from *Vertex to Abser or changes the return type of a single function from a type that matches an interface to the interface, and that would also create an assignment that creates a new value of the new type that will panic in contexts the old one would not have panicked, even though the programmer has not added any code saying "please construct an Abser (type, pointer) tuple from my pointer."

> Go should implicitly convert a not-nil interface (that is implemented by a nil pointer) to a nil interface

I am not arguing for that.

> unless you consider "assigning a concrete value to an interface" to be an "implicit conversion", but Java does this too

I guess, but this is much less of a conversion than Go is performing because the runtime value does not change at all. It's the same pointer, not a tuple containing the pointer. Unlike in Go, this assignment may very often be correctly implemented by a sequence of 0 instructions! Importantly, the meaning of "foo == null" is unchanged. This is not because Java performs implicit conversions of interface types to specific pointer types at the null check. It's because in Java interfaces are unable to express having a concrete value of a specific class which is null, because they are just pointers. One might say "Java interfaces do not have inner and outer nil values" as a shorthand for this.

> Go gives you no way to express that a reference type can't be nil

I agree that this would solve the problem.

> You don't need reflection:

You don't need reflection if you statically build an exhaustive list of all pointer types that match the interface in the whole program but don't actually match the interface in the sense that the methods don't handle nil, then generate code to handle each of them. This just doesn't seem like a reasonable option because sometimes people make libraries so they can't necessarily do static analysis of the whole program at the point where they are writing the code. It also sort of defeats the purpose of interfaces, I guess.

Re: Go generics are not bad

#295

Earlier quoted context omitted.

> A difference between Go and Java is that a null Regex in Java will not be implicitly converted to a non-null Stringable containing a null Regex. You have it exactly backwards--you're arguing that Go should implicitly convert a not-nil interface (that is implemented by a nil pointer) to a nil interface so that Go interfaces behave more like Java interfaces, but Go isn't doing any implicit conversions (unless you con…

> Go isn't doing any implicit conversions What do you call it when you assign a value of one type to a variable of another type and the language creates a new value, different from the one you were assigning, and stores that new value in the variable? In C++ this is called "implicit conversion." Here's the tutorial again: var a Abser f := MyFloat(-math.Sqrt2) v := Vertex{3, 4} a = f // a MyFloat implements Abser a =…

> What do you call it when you assign a value of one type to a variable of another type and the language creates a new value, different from the one you were assigning, and stores that new value in the variable? In C++ this is called "implicit conversion."

Right, I addressed this when I said:

> unless you consider "assigning a concrete value to an interface" to be an "implicit conversion", but Java does this too

So let's just skip to your reply to that bit:

> I guess, but this is much less of a conversion than Go is performing because the runtime value does not change at all. It's the same pointer, not a tuple containing the pointer. Unlike in Go, this assignment may very often be correctly implemented by a sequence of 0 instructions!

Yes, Java and Go have different implementations/conceptions of "interface". I don't see what bearing this has. From the programmer's perspective, Java's interface assignment isn't more explicit than Go's even though the underlying mechanisms differ. In particular, Go needs to create a new "header" value because its interfaces are polymorphic across any type (including primitives and value types!) whereas Java's are polymorphic only across objects and Java attaches a header to every object.

> Importantly, the meaning of "foo == null" is unchanged.

Go's `foo == nil` isn't changed either. It behaves like it does for all reference types. The difference between Java and Go is conceptual, but the `==` operator is coherent within each of them.

> This is not because Java performs implicit conversions of interface types to specific pointer types at the null check.

Agreed, and to be clear, I didn't claim it did. Nor does Go, so I'm not quite sure what you're responding to here.

> It's because in Java interfaces are unable to express having a concrete value of a specific class which is null, because they are just pointers.

> One might say "Java interfaces do not have inner and outer nil values" as a shorthand for this.

Right, but we were debating whether or not Go interfaces have inner or outer nils. Consider `var x interface{} = 0`. What's the outer value? And what's the inner value? The whole point of my comment is that you're conceiving of Go interfaces as having inner and outer values, but Go interfaces are just a fat pointer. As such, you can have pointers to pointers with Go interfaces. And while you can't have pointers to pointers with Java interfaces, you can elsewhere in Java so a Java programmer should be able to reason about Go interfaces because they can understand the concept of a nullable pointer to a nullable pointer.

Ultimately, it seems that you're criticizing Go for having a different interface mechanism than Java, and you get bugs when you treat Go interfaces like Java interfaces. And Go made the right choice here, because it can express polymorphism over a wider range of types than just objects (contra Java interfaces) and indeed it doesn't have any sort of object/primitive bifurcation--from the perspective of Go interfaces, all concrete types are just data.

Re: Go generics are not bad

#296

Earlier quoted context omitted.

> Go isn't doing any implicit conversions What do you call it when you assign a value of one type to a variable of another type and the language creates a new value, different from the one you were assigning, and stores that new value in the variable? In C++ this is called "implicit conversion." Here's the tutorial again: var a Abser f := MyFloat(-math.Sqrt2) v := Vertex{3, 4} a = f // a MyFloat implements Abser a =…

> What do you call it when you assign a value of one type to a variable of another type and the language creates a new value, different from the one you were assigning, and stores that new value in the variable? In C++ this is called "implicit conversion." Right, I addressed this when I said: > unless you consider "assigning a concrete value to an interface" to be an "implicit conversion", but Java does this too So l…

> Go's `foo == nil` isn't changed either

This is incorrect. The value of "foo == nil" in function A can be changed by a patch that only changes the return type of function B from *Vertex to Abser. If you would like to say that this is not because of implicit conversion from pointer to interface and it is not because of any semantic difference between comparing the vertex pointer to nil and comparing the Abser interface to the nil interface value, I would like to know what you think causes it.

> Right, but we were debating whether or not Go interfaces have inner or outer nils

I haven't been debating this since it's obviously true. You seem to agree that an Option> may take on the values None and Some(None) separately. You can debate about what to call things, but I'm still not sure what you would like me to call the difference that exists between Java and Go which causes code that does not throw NullPointerExceptions in Java to panic in Go because the inner value is nil and the user has failed to use reflection (or generate an exhaustive list of all possible pointer types that could match the interface but whose method does not handle nil and check each of them individually).

> Consider `var x interface{} = 0`. What's the outer value? And what's the inner value?

What do you mean? That's a (int, 0) tuple. The type tag is int. The inner value is 0. interface{} expresses outer-nil-ness using a special value of the type tag, so that's (nil, nil) which a different value from (*Vertex, nil).

> And while you can't have pointers to pointers with Java interfaces, you can elsewhere in Java so a Java programmer should be able to reason about Go interfaces because they can understand the concept of a nullable pointer to a nullable pointer.

Please demonstrate by declaring a pointer to a pointer in Java. Ideally, you could also show how in Java to convert a pointer to a pointer to that pointer using a single assignment, without calling any constructors or functions or making any explicit casts.

> Ultimately, it seems that you're criticizing Go for having a different interface mechanism than Java, and you get bugs when you treat Go interfaces like Java interfaces. And Go made the right choice here, because it can express polymorphism over a wider range of types than just objects (contra Java interfaces) and indeed it doesn't have any sort of object/primitive bifurcation--from the perspective of Go interfaces, all concrete types are just data.

No, I'm criticizing Go for being fundamentally broken in a novel way that no language had come up with previously. It should seriously not be possible to cause a panic in a method call that is guarded by a nil check by changing the return type of some other function from a pointer-to-struct to an interface and making 0 other changes.

If you ask the language designers on the mailing list why it's like this, they'll tell you that it's important to make the zero values of types useful, including nil pointers of specific types. They also decided to ship a stdlib in which like half of the methods with pointer receivers immediately panic when passed nil, and they would like to tell you that the right way to avoid panicking is to not store such pointers in such interfaces. The language will do nothing to help you to avoid storing these pointers in these interfaces. Even if you successfully avoid storing these pointers in these interfaces in your code, if you store them in any interface at all, other code may use "interface upgrades" to find out that the methods that panic exist and call them. Wow!

Re: Go generics are not bad

#297
post #293

Earlier quoted context omitted.

> doesn't mean that the application of that understanding to implementation humans will take a liking to is well understood. I really like how ML implements it, does that make me somehow less human?

What you like or don't like isn't really the point. The point is that it isn't unimportant when judging how "good" a language is to take into account how many people are willing to use it. It is hard to argue that a language is "good" when a negligible fraction of developers are willing to use it.

As Alan Kay said, the industry is a pop culture, driven by marketing not science. I'm still going to argue for good ideas though.

Re: Go generics are not bad

#298
post #288
post #284

Earlier quoted context omitted.

> No. It's not a matter of personal opinion. Go indeed lacks "several features, including safety features, that help people perform their job better". That is a subjective statement. There's no objective list of language features that make a programmer's job better. I'd like to see optional types in Go, but I can't say that it would objectively make the vast majority of programs I've written better, not to say about…

> There's no objective list of language features that make a programmer's job better. Quite the contrary, the list is often very objective. People know what makes them more productive. It’s just not a single list for everyone. This is why people engage in language criticism: to push for features THEY want. You say “don’t use the language”, we say “we use what we want, maybe you don’t use the features”. > You don't st…

> Read the whole thread. It started with concrete paths: functional operators like map/reduce/etc.

I did and did not find your characterization accurate.

> The suggestion was promptly dismissed by the people against changes in Golang by calling it “type system fidget spinners”.

There was some of that, but there was also a genuine and accurate response to the suggestion saying that with generics now being part of the language it is a case of getting them settled, (i.e. the exp/constraints package) and see what could they be utilized for in the standard library. What you're asking for already exists as 3rd party packages so as generics are adopted in the stdlib in 1.19 and later there's no reason not to have map/filter/reduce in the stdlib as well, there's certainly no opposition to it from the core contributors or the community on the whole.

The fact that the focus is on the 'fidget spinners' comment and not the one explaining the situation in detail is curious indeed.

Re: Go generics are not bad

#299
post #298
post #288

Earlier quoted context omitted.

> There's no objective list of language features that make a programmer's job better. Quite the contrary, the list is often very objective. People know what makes them more productive. It’s just not a single list for everyone. This is why people engage in language criticism: to push for features THEY want. You say “don’t use the language”, we say “we use what we want, maybe you don’t use the features”. > You don't st…

> Read the whole thread. It started with concrete paths: functional operators like map/reduce/etc. I did and did not find your characterization accurate. > The suggestion was promptly dismissed by the people against changes in Golang by calling it “type system fidget spinners”. There was some of that, but there was also a genuine and accurate response to the suggestion saying that with generics now being part of the…

The reason I'm focusing on the fidget spinner guy and you because those are a grandparent of the posts we're currently writing and replying to. If I wanted to reply to the other posts I would do so below those posts. I merely upvoted PhilippGille because he provided a good answer and continued with my day.

We're in a chain of comments. I suggest clicking on "parent" until you get to the post I'm talking about and you'll understand better.

Just because someone else was civilised once in an unrelated thread (and, as far as I know, PhilippGille is not affiliated to you) doesn't make YOUR behaviour right. Start owning up your mistakes.

The explanation from Phillipp is satisfactory but isn't part of this thread, and doesn't excuse the lack of civility and personal attacks from you. Nor does it erase the fact that what you're asking for (a discussion starting with what can be improved in Go) was provided from the get-go.

Seriously: the only real issue here is YOUR lack of civility. Not Golang, not the people criticising it. Own up your own mistakes. Maybe work on your issues too.

Rather than picking apart and dissecting my replies and other people's, maybe try addressing YOUR behaviour at least once. You're the one whose post was flagged. It's not justified by anyone else's post.

Re: Go generics are not bad

#300
post #299
post #298

Earlier quoted context omitted.

> Read the whole thread. It started with concrete paths: functional operators like map/reduce/etc. I did and did not find your characterization accurate. > The suggestion was promptly dismissed by the people against changes in Golang by calling it “type system fidget spinners”. There was some of that, but there was also a genuine and accurate response to the suggestion saying that with generics now being part of the…

The reason I'm focusing on the fidget spinner guy and you because those are a grandparent of the posts we're currently writing and replying to. If I wanted to reply to the other posts I would do so below those posts. I merely upvoted PhilippGille because he provided a good answer and continued with my day. We're in a chain of comments. I suggest clicking on "parent" until you get to the post I'm talking about and you…

The parent to my original comment states:

> No it's simpler than that, they hate Go cause it's a half-assed language. The creators didn't respect developers enough to make it consistent and complete cause "devs are not smart enough".

If that isn't the definition of flamebait and a personal attack, I don't know what is. It certainly is far enough from a measured, concrete suggestions for improvements to the language.

Post reply on HN