News for hackers. Stuff that matters.
Re:The Value in Go’s Simplicity by atombender on Saturday November 16, 2019 @10:49AM (#21553201)
I agree that Go forces you to focus on mechanics of your code more so than some "fancier" languages.

However, Go's poor type system also forces you to write worse and less safe code than you want.

For example, the lack of enums and sum types means it's hard to represent, safely and efficiently, anything that should be the same type but have different values within a strict set of allowed values. The nearest you get in Go to an enum is:

  type Color int
  const (
    Red Color = iota
    Blue
    Green
  )
Alternatively, can do the same with a string:

  type Color string
  const Red Color = "red"
  // etc.
This gives you some type safety, since a Color requires casting to force an int or string or whatever to become it. But that doesn't give you real safety, since you can do:

  var c Color = Color("fnord")
This comes up whenever you're handling foreign input. JSON unmarshaling, for example. You can override the unmarshalling to add some validation, but it won't solve your other cases; it will always be possible to accidentally accept something bad. Not to mention that the "zero value" of such a value is usually wrong:

  type Color string
  var c color // Empty string, not valid!
A more safe way is to hide the value:

  type Color interface {
    isColor()
  }
  type colorValue string
  func (colorValue) isColor() {}
  var Red Color = colorValue("red") // etc. 
Now nobody can create an invalid value, and the zero value is nil, not an invalid enum value. But this is unnecessarily complicated and shouldn't be necessary in a modern language in 2019.

The case of genuine sum types is worse. Your best bet is to use sealed interfaces:

  type Action interface {
    isAction()
  }

  type TurnLeft struct{}
  func (TurnLeft) isAction() {}

  type MoveForward struct{
    Steps int
  }
  func (MoveForward) isAction() {}
There are some downsides. You have to use static analysis tools (go-sumtype is good) to make sure your type switches are exhaustive. You get a performance penalty from having to wrap all values in an interface. And if you're going to serialize or deserialize this (e.g. JSON), you will be writing a whole bunch of logic to read and write such "polymorphic" values.
Re:The Value in Go’s Simplicity by strken on Saturday November 16, 2019 @12:04PM (#21553611)
Agree with this. There are couple of observations I'd make, though.

Firstly, "enums" using iota should always be defined as either

    const (
        Red Colour = iota + 1
        Blue
        Green
    )
or

    const (
        Unknown Colour = iota
        Red
        Green
        Blue
    )
to avoid the zero value problem.

Secondly, and this is a personal preference, I've really enjoyed not having to work with sum types. In practice other programmers seem to use them when a public interface would have been sufficient, and it's convenient to be able to do:

    // original package 
    type Position struct {
        X, Y int
        Rot Rotation
    } 

    type Action interface {
       Apply(Position) 
    }

    type MoveForward struct {
       Steps int
    }
    func (m MoveForward) Apply(p Position) {
        switch p.Rot {
        case Up:
            p.Y += m.Steps
            ... 
        }
    } 

    // second package wrapping the first
    type WarpToPoint struct {
        X, Y int
    }
    func (w WarpToPoint) Apply(p movement.Position) {
        p.X, p.Y = w.X, w.Y
    }
Re:The Value in Go’s Simplicity by morelisp on Saturday November 16, 2019 @02:56PM (#21554567)
> I've really enjoyed not having to work with sum types

Your example of what you prefer uses (a shitty approximation of) a sum type in p.Rot.

(This is also the most basic possible use of a sum type; they are not only useful for enums, it's just to point out that even a large amount of "simple" Go code would benefit from them.)

Re:The Value in Go’s Simplicity by strken on Wednesday November 20, 2019 @01:22PM (#21586711)
I understand that p.Rot is a shitty approximation of a sum type, but it still works and almost certainly won't break anything since Go forces you to explicitly typecast. The important thing is that the list of possible actions wasn't sealed by the type system, which in the original example it was.

I want to reiterate that I am aware sum types can be useful. I just don't think they're useful _enough_ to outweigh being a footgun for calling code.

Re:The Value in Go’s Simplicity by atombender on Saturday November 16, 2019 @07:18PM (#21555780)
I would argue that this misses the use case of sum types, which typically don't have behaviour (or they'd just be interfaces!).

For example, consider an AST for a programming language. AST nodes don't have any behaviour (though they might have some methods for convenience, though nothing that implements behaviour). You want to do optimizations and printing and compilation and so on, but on top of the pure AST.

Re:The Value in Go’s Simplicity by riquito on Saturday November 16, 2019 @02:38PM (#21554480)
Enums exist specifically to be compared with one of their possible values, how can you have a zero value problem?
Re:The Value in Go’s Simplicity by gen220 on Saturday November 16, 2019 @03:06PM (#21554633)
If the caller of your interface does not specify a value for your enum, they have implicitly specified the zero value. Whether that’s desirable behavior or not is up to you. For most clients, this behavior can be surprising if the zero value is a meaningful one (i.e. one that implies an intentional choice).

IME it’s useful to explicitly define the zero value’s meaning as “UNSPECIFIED”, to simplify the problem of trying to guess if the client intended to pass a zero value.

Re:The Value in Go’s Simplicity by reificator on Saturday November 16, 2019 @06:51PM (#21555691)
> Enums exist specifically to be compared with one of their possible values, how can you have a zero value problem?

Because there's not actually support for enums in go.

There's support for constant values, and automatically assigning sequential values to them. That happens to be useful for solving the same kinds of problems that enums solve, but they're not equivalent.

Re:The Value in Go’s Simplicity by skybrian on Saturday November 16, 2019 @01:46PM (#21554220)
It probably amounts to the same thing, but I think there's a more pragmatic approach to thinking about safety. A type is a way to remember that validation has already been done. This is true for constants by inspection (code review). For dynamic code, have a validator function that takes unvalidated input and returns a Color or an error, and always use that to create a Color.

That's usually sufficient. Any "cheating" should come up in code review as a suspicious cast to Color. In an audit, you could search for casts to Color.

Safe languages often have unsafe constructs. It's the same principle. The unsafe code is signposted, and you review it.

If you want further encapsulation, another useful trick is to make Color a struct with a private field. It's not usually necessary, though.

Go does have an unfortunate quirk that you can always create a zero value without calling a constructor, so you'll need to make sure a zero Color has meaning. (An interface doesn't really change this because the then the zero value is nil. That's not an improvement over making the zero value mean "black" or "transparent" or "invalid".)

Re:The Value in Go’s Simplicity by gen220 on Saturday November 16, 2019 @11:26AM (#21553399)
I know it’s not a proper part of the language, so it might qualify a “hack” solution, but one can get around some of the issues you describe by defining your enums in protobufs, and using/trusting the generated code, no?

It’s not a pretty solution from a language design point of view, but it’s been more than effective for us from an engineering point of view: since we’d need those proto definitions anyway, why bother writing our own?

Re:The Value in Go’s Simplicity by crimsonalucard on Saturday November 16, 2019 @11:45AM (#21553529)
Metaprogramming fixes everything. Why didn't they put in sum types in the first place? I think the designers probably didn't know about it at the time.
Re:The Value in Go’s Simplicity by pjmlp on Saturday November 16, 2019 @11:49AM (#21553549)
Given the linage of the designers they were pretty much aware of it.
Re:The Value in Go’s Simplicity by crimsonalucard on Saturday November 16, 2019 @12:20PM (#21553707)
The "linage" implies that they aren't aware of it, or at least they weren't when designing the language. Rob Pike is more of a systems guy than a type theorist.

Like most systems guys, they know of how C handles types and how C++ handles types. The concept of Parametric Polymorphism or sum types outside of OOP is most likely something Rob Pike was not familiar with as it's hard to see why sum types were not included in the language.

Having IO functions return a tuple of error and value rather then an Option type is not simplicity it's complexity arising from lack of a type primitive. The feature is ugly and very much looks like it was implemented by someone who wasn't aware of a type that can be a Value OR an Error. So instead he implemented a type that can be an Error AND a Value and left it to manual run time checks for the programmer to figure out if an error even occurred.

The other thing is that this "tuple" type of error AND value looks like a hack. Tuple types can't be saved to a variable in GO and can only be returned by functions and immediately unrolled into its separate primitive values. It's like Rob knew something was off in this area so he created a temporary concept of a tuple in the return value of a function to make up for it. A consistently designed language wouldn't have a Tuple only returnable by a function call. It seems strange and inconsistent.

Additionally, the fact that, in GO, some types can have Nils and other types default to a zero value implies that Rob Knew nulls were bad but didn't know how to completely get rid of the null.

I'm thinking that Robs initial notions of sum types and parametric polymorphism is that they can only be implemented via hierarchies of inheritance which in itself has many problems. It makes sense because this is what systems programmers are exposed to (C, C++) as opposed to typed lambda calculus or Haskell. So it's easy to see that Go is the result of Robs awareness of problems with OOP but lack of awareness of the theoretical alternative.

Re:The Value in Go’s Simplicity by AnimalMuppet on Saturday November 16, 2019 @02:32PM (#21554447)
He was at the premiere computer science research organization for decades. And you presume that he's not familiar with sum types, because if he was, he would have included it, and therefore he can't have been familiar with it, because he didn't include it? That's the most amazingly arrogant thing I've heard in a while.

The fact is that Rob almost certainly knows more than you, rather than less. And he still made the choices he made. That should make you ask questions, not about Rob's knowledge, but about yours.

Re:The Value in Go’s Simplicity by crimsonalucard on Saturday November 16, 2019 @11:30PM (#21556571)
https://news.ycombinator.com/item?id=6821389

Read that quote by Rob and the responses. IN the quote rob describes what he believes Generic Types are at face value... he literally takes it into a tirade about inheritance and hierarchies of classes... something that is not part of type theory at all.

Honestly, it feels like he didn't know about Algebraic Data types. I'm not the only one who thinks this as shown from the responses to his quotation.

One of the responses:

"Or perhaps Rob Pike just hasn't explored the relevant literature in enough depth. At one point he admitted that he didn't know that structural typing had already been invented previously! This isn't to criticize Rob, I find his talks fascinating, I think he's awesome, he's a friend of my boss, etc. But he's hardly the first hard-core hacker to be ignorant of the degree to which type theory has seen dramatic advances since the 1980s."

Re:The Value in Go’s Simplicity by jeremyjh on Sunday November 17, 2019 @08:16AM (#21557792)
Honestly this is really compelling evidence that Pike doesn't know much about type theory. That isn't terribly surprising, and the other early collaborators on the language that I know of also came from more of a systems background. I think its entirely likely that Go's crippled type system is partly an accident, and not entirely a design choice. It would be helpful if - with the benefit of hindsight - they would admit it, rather than invent post-hoc justifications for the way things are.
Re:The Value in Go’s Simplicity by jcora on Sunday November 17, 2019 @04:47AM (#21557231)
I don't know man. I feel like there are places I know more than Rob Pike almost certainly. Like, I don't know, most of functional programming. I seriously doubt he knows what indexed monads are better than me.

So, at the point they were creating Go, I think it's perfectly reasonable they had even less exposure to fp, and didn't actually know about these better solutions.

Re:The Value in Go’s Simplicity by thatswrong0 on Saturday November 16, 2019 @06:39PM (#21555644)
This feels like the opposite of an ad hominem fallacy
Re:The Value in Go’s Simplicity by AnimalMuppet on Saturday November 16, 2019 @07:39PM (#21555843)
Well... those Bell Labs types were polyglots. They tried a lot of things in a lot of languages. Does that mean that Rob Pike knew about sum types? Not necessarily, no. But it gives you two possibilities.

1. Rob Pike spent all that time at Bell Labs, with all these CS experts, read and wrote all those papers, and never heard about sum types. That's... possible. It's not the way I would bet, but it's possible.

2. Rob Pike knew perfectly well what sum types were, and left them out of Go, because he thought they didn't fit with what he was trying to do.

To me, the second is both more charitable, and more in line with what I think Rob Pike's background and experience would have exposed him to. crimsonalucard obviously disagrees. He seems to think that sum types are so obviously the right thing that Pike could not have possibly not put them in Go had he known about them, and therefore he could not have known. And that is in fact possible.

But it seems to me to better fit with Pike's background, as well as with the principle of charity, to assume that he knew. And still he chose to leave them out.

Now, he could still be wrong. And we can discuss whether sum types are really a good fit for what Go is trying to do. But the assumption that he couldn't have known, or he would have done it the way someone else thinks he should have, is what grates on me.

For what it's worth, the Go FAQ (at https://golang.org/doc/faq#variant_types) says that they considered sum types, and didn't think they fit.

Re:The Value in Go’s Simplicity by crimsonalucard on Saturday November 16, 2019 @10:57PM (#21556480)
> But the assumption that he couldn't have known

Please read my initial assumption. In no place did I say he COULDN'T have known. Read it. I literally started the statement with "I think the designers probably didn't know" rather than "I know they COULDN'T have known." There is nothing to "grate" you here. I simply had an opinion and a guess, and you disagreed with it and decided to insult me.

What grates me is the assumption that I said it's 100% true that Rob Pike didn't know what a sum type was. I think it's very likely he didn't know. If he did know then I am wrong. That's all.

>For what it's worth, the Go FAQ (at https://golang.org/doc/faq#variant_types) says that they considered sum types, and didn't think they fit.

That FAQ should have been presented earlier in a cordial and civil way. If you did I would have admitted that my hypothesis was incorrect. Science logic and evidence rule the day and I try to not invest any emotion into any of my opinions. It's hard but I follow this rule. If the FAQ says he knows about it then he does and I am wrong. Instead you chose not to present this evidence and call me arrogant.

There was no need to call me "Arrogant." It disgusts me to hear people talk like this. Either way the GO the language feels awkward in the way it uses product types and does indeed feel like Rob didn't know about them because the sum types certainly do feel more fitting then having a function return a tuple out of nowhere.

I also disagree with the FAQ. Plenty of languages have constraint types that are placed on the subtypes of the sum type. There's no confusion imo. Also note that the previous sentence was just an opinion. Please don't call me arrogant because I have one.

Re:The Value in Go’s Simplicity by AnimalMuppet on Sunday November 17, 2019 @12:31AM (#21556703)
Well, I didn't have the FAQ earlier. I was guessing then.

And I don't see the FAQ as necessarily total vindication of my position. The language team considered sum types; it doesn't mean that Rob Pike did in the initial design. It could be that, after it was kind of mostly formed, they thought about sum types and couldn't find a sensible way to make them fit. Or it could mean that he considered them and rejected them from the beginning. The FAQ isn't specific enough to say.

As for calling you arrogant: You are not the first person who has said, here on HN, that Pike "looked like he didn't know"/"must not have known"/"couldn't have known". Those conversations kind of run together in my mind. As a result, I was hard on you at least in part because others went too far. That's not fair to you, and I apologize.

I also cannot call you arrogant for having an opinion. I also have one - you may have noticed this. ;-)

However, I feel that I should say (and say as gently as I can) that you often sound very harsh on HN. A harsh tone causes many to read your content with less charity than the ideas might deserve. (I am not here trying to defend my interaction in this thread.) And this is not very helpful of me, because if you ask for advise on what, specifically, to change, I'm not sure I can give any. I mention it because you may be unaware of it, and awareness may help.

I can easily see how the previous paragraph could offend you. I am not trying to do so. Forgive me if it causes offense.

Re:The Value in Go’s Simplicity by ninjaaron on Monday November 18, 2019 @11:27AM (#21565558)
> To me, the second is both more charitable, and more in line with what I think Rob Pike's background and experience would have exposed him to.

More charitable to Rob Pike, rather than to the person you're in the the middle of a conversation with.

Anyway "appeal to authority" is not an argument, it's a religion. Our lord and savior, Rob, knows so much that his design decisions are beyond question.

Re:The Value in Go’s Simplicity by _ph_ on Sunday November 17, 2019 @03:14AM (#21557035)
Not only is it likely that, as you say, Rob Pike was aware of sum types, but he did not create Go on his own but it was created by a small team. Someone like Robert Griesemer, who studied under Wirth, would have known about them, if the others hadn't.

I have been using the Wirth languages a lot (Pascal, Modula), and one of the big appeals of Go to me is, that it brings back a lot from those languages to the modern times. The Wirth languages are far to underrated in programming today.

Re:The Value in Go’s Simplicity by pjmlp on Sunday November 17, 2019 @03:34AM (#21557084)
The irony being that most Wirth languages are more expressive than Go will ever be, with the exception of the first release of Pascal and Oberon versions, and the follow up on minimalist design approach with Oberon-07.

When Go came out, I though it could follow Oberon, starting small and eventually reach Active Oberon/Zonnon expressiveness, but alas that is not how they see it.

Even Limbo has features that Go still lacks.

Re:The Value in Go’s Simplicity by wwright on Saturday November 16, 2019 @08:19PM (#21555963)
It’s called “appeal to authority”: https://en.wikipedia.org/wiki/Argument_from_authority
Re:The Value in Go’s Simplicity by ses1984 on Saturday November 16, 2019 @10:37PM (#21556424)
The name of this fallacy is appeal to authority.
Re:The Value in Go’s Simplicity by foldr on Saturday November 16, 2019 @01:01PM (#21553961)
Having the value returned together with the error is convenient for a couple of reasons.

First, it's often possible for the function to return a meaningful value even in an error case (e.g., number of bytes read before the error occurred).

Second, it's often possible to return a sensible 'null' value together with an error which can be handled correctly without checking the error value. (A map lookup is the obvious example of this.) This simplifies logic in some places.

Using sum types for errors in Go wouldn't actually work very well unless you fundamentally changed other aspects of the language. You'd need pattern matching, a whole bunch of generic higher order functions for manipulating option/result types, etc. etc.

Re:The Value in Go’s Simplicity by crimsonalucard on Saturday November 16, 2019 @01:34PM (#21554162)
>First, it's often possible for the function to return a meaningful value even in an error case

Create a type that explicitly stores this information. The return type can hold the (error message and a value) OR (just a value). This type expression is a more accurate description of what's really going on. Whichever way you want to represent the product type it's not isomorphic to the actual intended result that the function should return. A sum type can represent the return value of the sentence below while GO cannot:

"A function that returns (a value) OR an (error with a value)"

This is the true intention of the function you described.

>Second, it's often possible to return a sensible 'null' value together with an error which can be handled correctly without checking the error value. (A map lookup is the obvious example of this.) This simplifies logic in some places.

But opens up the possibility of a runtime error if you fail to check for it. Historically there are tons of functions in C, C++ or javascript that use null to represent an error and it is self quoted to be the greatest mistake ever made by the creator of null. No language needs a null.

>Using sum types for errors in Go wouldn't actually work very well unless you fundamentally changed other aspects of the language. You'd need pattern matching....

Using product types to represent errors has already changed the nature of GO in a very hacky way.

Only Functions in GO can return tuples and the concept of the tuple can never be used anywhere else. You cannot save a variable as a tuple, you cannot pass a tuple as an argument. You can only return a tuple then instantly unroll it. It's an arbitrary hacky feature obviously made to support error values.

It would be better to have an arbitrary pattern matching feature... this makes more sense then arbitrary tuples returned from functions.

>a whole bunch of generic higher order functions for manipulating option/result types, etc. etc.

Actually no you don't. The fact that go functions return tuples with errors, does this mean that higher order functions need to handle tuples? No! not at all. In fact go explicitly eliminates support for this... The tuples in GO need to be unrolled into their constituent types before they can be used in any other function. The same concept can be applied to Option types. You have to unroll the value and explicitly handle either individual type. You do not ever need a higher order function that accepts the Option type as a parameter.

Like all languages that have the Option Type/Maybe Monad etc... Any function that returns this type represents a function that is impure that needs to be unrolled first before passing the value down to the functions that do closed and pure calculations. A function that takes an Option type as a parameter is a function that says "I am a function that can only take values from IO" It's very rare for functions to be implemented like this even in languages that have first class support for the sum type and Monads. In haskell I can't recall ever seeing a function that takes the IO monad as a parameter. In haskell and in Rust these values need to be unrolled into their constituent types before they can be used.

Please note I am not advocating the inclusion of Monads into GO. Just talking about sum types.

Re:The Value in Go’s Simplicity by m0zg on Saturday November 16, 2019 @08:01PM (#21555904)
To me, Go is "masturbation prevention" language. Meaning that certain deliberate design choices were made to prevent precisely the types of endless unproductive masturbation you see in some other languages. I.e. type masturbation in Haskell or OOP/IoC masturbation in Java (particularly egregious, Java is not a bad language otherwise), or metaprogramming mastrurbation in C++. The omission of these features is not a bug. It's a feature in itself.
Re:The Value in Go’s Simplicity by _ph_ on Sunday November 17, 2019 @03:33AM (#21557080)
Indeed. More than once, I have seen program projects ruined by creating an overly elaborate class hierarchy, sometimes 10 layers deep. Just to express any theoretical aspect of the domain in the structure of the class hierarchy. Java programs often suffer from this. Which is especially sad, as Java has interfaces, which I think are the right way of representing abstract types for APIs for example. Interfaces don't force you to into a type inheritance just to fulfil a contract. But unfortunately, they are way to rarely used.
Re:The Value in Go’s Simplicity by m0zg on Sunday November 17, 2019 @06:10AM (#21557432)
If anything in Java they're overused. You often see only one class implementing an interface where the programmer can reasonably expect there will never be another implementation, and where it's not exposed outside the API boundary, so the interface is gratuitous.
Re:The Value in Go’s Simplicity by SEJeff on Saturday November 16, 2019 @05:56PM (#21555476)
For json unmarshalling of structs at least, I've become a pretty big fan of the validator library from the go playground:

github.com/go-playground/validator/v10

It uses struct tags to validate the struct and is quite extensive.

https://godoc.org/gopkg.in/go-playground/validator.v10

Re:The Value in Go’s Simplicity by lowmagnet on Saturday November 16, 2019 @04:27PM (#21555061)

  type Color struct {
    R, G, B, A byte // IDK
  }
  func (o OtherType) *Color {
    return &Color{R: o.r, B: o.b, G: o.g}
  }
  type Colorer interface {
    func Color() *Color
  }
A colorer would return a color regardless of its type. This is behavior-based interface. I just need a thing that when I call Color(), you get a *Color.
Re:The Value in Go’s Simplicity by atombender on Saturday November 16, 2019 @07:14PM (#21555765)
That's not an enum or sum type, though, and misses the point of my example. For colors, sure, you can use a structural type to represent RGBA, but that wasn't what I was trying to get across. What if the set of possible values cannot be described as scalars? The other example with "actions" demonstrated this problem.
Re:The Value in Go’s Simplicity by weberc2 on Saturday November 16, 2019 @03:02PM (#21554618)
I want to see generics and real enums (I.e., Rust enums, not C/Java enums) added to Go, but as far as it being unsafe or inefficient, these concerns are overblown for many apps. People who levy this criticism are often fine with Python and/or JS for similar categories of applications, even though they are far less safe and less performant than Go. We should be clear when we criticize Go that we’re talking about addressing the last 1% or so of type-related bugs and/or extending the performance ceiling a bit higher. We should also give Go credit for permitting a high degree of safety, performance, and productivity when other languages make you choose one.
Re:The Value in Go’s Simplicity by pjmlp on Saturday November 16, 2019 @04:11PM (#21554977)
Java enums are much more powerful than plain old C enums actually.
Re:The Value in Go’s Simplicity by weberc2 on Saturday November 16, 2019 @05:31PM (#21555376)
I’m aware, but it’s irrelevant.