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<Option<T>> 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!
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<Option<T>> 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!