1. 28
    Go is Not Good go rant github.com
  1.  

  2. 37

    So it starts off saying “not meant to be insulting to anyone or anything” and then, a bit below, they decide to take “designed for stupid people” as an actual reason of why Go is not good? I can’t say I’m impressed.

    1. 21

      To be fair, the author is just listing what other people have said about go and their major points. The author themselves did not say that Go is “designed for stupid people”, they included someone who did say it.

      1. 20

        I refuse to accept that as valid; quoting someone is an action too. Author could have decided to leave that out, because let’s face it, it brings nothing to the table. Both of the articles cited for that point have a lot of other stuff that they are cited for, as well. To be fair, I’m probably nitpicking, but it still irritates the hell out of me when people pull together a list of reasonable concerns, and then plop in a “you’re stupid” trump card. I’ve worked with the “you’re stupid” trump card for a year and a half, now. I really dislike it. A lot.

        [Edit: when I say “work with”, I mean “had a coworker who kept a trump card unreasonable argument nearby at all times”]

        1. 3

          Personally, I would feel i’d either have to leave the article out entirely (which may have been what I did) or include the “point” in the list. It would have felt incomplete/wrong to me otherwise.

          1. 1

            i got voted down as a troll for saying this in another subthread, but direct, deadpan quoting of someone who says something over-the-top is a definite form of humour. “psuedointellectual arrogance of Rob Pike and everything he stands for” was a big tell, but the overall “tone” of the page is subtly (and i’d bet intentionally) funny, regardless of whether it serves a useful purpose or not.

        2. 5

          I doubt they’re falling Go users stupid; it’s probably a reference to Rob Pike’s (one of Go’s creators) comment “They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.”

          1. 4

            It was definitely a poorly expressed point by Rob Pike but making a language easy to use is absolutely the whole point of language design. And really, Go is very easy to learn and provides a very low barrier to getting real work done. So that’s a great thing.

            1. 4

              making a language easy to use is absolutely the whole point of language design

              That is really not the case. “Ease of use” is only one dimension of a language, and one that’s commonly overoptimized for to the detriment of other properties like correctness, efficiency, readability, maintainability…

              Google “rich hickey simple made easy”

            2. 1

              Okay, so a hit on pride, but how is that a shortcoming of the language? If it’s a driving point of design decisions, it would result in the language being weak-sauce, but in and of itself, it’s not a weakness of the language.

              1. 2

                Personally I’ve never understood the comment as developer mistakes seem like the greatest motivator for strong type systems and uh, not including null.

            3. 1

              “I’m sorry if you feel offended”.

            4. 19

              This is petty and poorly thought out.

              1. 6

                I don’t think it is petty to criticize the tools we use, they’re not all created equal. In Go’s case, it might be beating a dead horse, though.

                1. 8

                  I definitely agree with you. Being critical of your tools is how you get better tools.

                  For this though, to take a bunch of subjective articles wholesale and glue them together as a affirmation that something is bad shows a lack of willingness to consider why that something was ever brought into existence. It’s just bad form.

                2. 0

                  i read it as a pretty hilarious sendup of the entire thicket of go criticism blogposts

                3. 8

                  The description of @wyager’s blog post is inaccurate:

                  type inference is too simple

                  It should be:

                  := is no substitute for real type inference

                  1. 5

                    Yeah, I would rather just say that Go has no type inference whatsoever. It’s like saying that C has type inference because you don’t have to write (int) ((int) 3 + (int) 4).

                    I would be happy if everyone struck the word “simple” from the vocabulary of programming language criticism.

                    1. 2

                      It’s not quite that easy though, is it? I mean x := 1 vs. x := "foo bar" is deducing that “foo bar” is a string, therefore x must be a string. At the same time 1 is an int, but it could also be a float, though the deduction rule decides that x must be an int. It’s a pretty limited “algorithm” (if you can even call it that), for sure, since it can only be used in declarations, but nonetheless, it’s closer in spirit to Hindley-Milner (what we often think of as the bar for Type Inference) than your C example, which I’d argue isn’t even in the same ball park.

                      1. 5

                        I think my C example fits the bill. Determining the type of an expression from the types of its subexpressions isn’t type inference, and that is what I was trying to illustrate with my C example. When you type \x -> x + 1 in Haskell, you have to type check x + 1 where x is of unknown type. The fact that this process can not only continue, but determine the type of x as a byproduct, is what is commonly known as “inference”.

                        This is not analogous to x := 1. The type of x is completely determined by the type of 1, and there are no unknowns in 1. The fact that 1 is allowed to have multiple types isn’t a big deal, one could give this a single concrete type if Go had intersection types. If there was any sort of indeterminacy in this process then Go could not type check 3 + 4 without type annotations a-la my C example.

                        1. 4

                          Determining the type of an expression from the types of its subexpressions isn’t type inference, and that is what I was trying to illustrate with my C example.

                          I think that’s exactly what type inference is. Forget Hindley-Milner for a second, and let’s consider what inference is. Inference is defined as “The act or process of deriving logical conclusions from premises known or assumed to be true.” Thus, if we know the type of the RHS, then we can conclude that the type of the LHS is equal to the RHS. It’s the simplest deduction we can make, sure, but it’s still a logical conclusion, and therefore, still inference.

                          The fact that we equate the Hindley-Milner algorithm to type inference is the problem here. Hindely-Milner is one way (and the most common way) to do something called type inference.

                          In your C example, and in C generally, the types of the RHS and LHS are always known because no deductions are done. The type checker says, “is int = char *? No. OK, generate a type error!” Or, possibly “Is the type float convertible to an int? Yes? OK, well, we’ll cast that to an int then, and it’s OK.”

                          1. 6

                            It’s the simplest deduction we can make, sure, but it’s still a logical conclusion, and therefore, still inference.

                            Your definition of type inference is so general that it encompasses everything. Why even have the term?

                            Here are some examples of non-HM type inference:

                            • Anything using semiunification (polymorphic recursion)
                            • Anything using higher order unification (higher-rank polymorphism, type functions, mixed-universal-existential quantifiers)
                            • Boolean unification (think SAT solvers + Lowenheim’s algorithm)
                            • Abelian unification (F# units of measure)
                            • GADT inference (OutsideIn(X), …)
                            • Local/colored inference (Scala)
                            • Anything based on data flow (intersection and union types, TypeScript, Flow, …)
                            1. 2

                              Your definition of type inference is so general that it encompasses everything. Why even have the term?

                              That’s not the point. Is it type inference? Or is it not?

                              Here are some examples of non-HM type inference:

                              Given that there are 8 algorithms listed in this thread that are definitely type inference, can Go’s algorithm, as trivial as it is, be considered a 9th?


                              Edit: This thread has gone on long enough, but really, I have a problem with the idea that something is “real” or “not real.” It seems to me, based on my understanding of type inference that Go, as trivial as its algorithm is, actually does real type inference, as in, it’s not faking anything. But real is being defined subjectively in the linked thread, and, I think maybe, in this thread as well. Maybe Go’s process should be called type derivation, or something else. I don’t know.

                              1. 2

                                That’s not the point. Is it type inference? Or is it not? Given that there are 8 algorithms listed in this thread that are definitely type inference, can Go’s algorithm, as trivial as it is, be considered a 9th?

                                As I have been arguing: no, Go does not provide any form of type inference. The same mechanism for typing 3 + 4 without type annotations is used to determine the type of a variable when := is used, and if the former is considered type inference, then every typed language in existence performs type inference, and the concept is meaningless.

                                I have a problem with the idea that something is “real” or “not real.”

                                I’m not sure what this has to do with anything. Go does not have type inference in any sense of that we know the word. Anyone claiming that Go has type inference, including its designers, is mistaken. That doesn’t make Go “fake”, it just means that people are not being precise with their terminology.

                                This whole incident reminds me of when Julia’s language designers tried to claim that Julia was dependently typed. From that description, you’d think Julia was a Coq or Agda clone, when it is actually more in the vein of Python. That doesn’t mean Julia is “fake”, it just means that the people promoting the language are misusing established terminology.

                                1. 2

                                  The same mechanism for typing 3 + 4 without type annotations is used to determine the type of a variable when := is used,

                                  How is it the same mechanism? 3:int + 3:int is obviously different than x:?? := 3:int, even if the ?? is trivial to deduce. I think your point would be more clear if you stated that x:?? + 3:int is the same mechanism, but you aren’t. Also, that’s irrelevant because Go doesn’t actually do that, anyway.

                                  I’m not sure what this has to do with anything. Go does not have type inference in any sense of that we know the word.

                                  In any sense that you know the word. I’m not claiming that Go is “fake,” which you misunderstood. I’m saying that the definition of “real type inference” is (possibly) subjective, here.

                                  It is my belief that a statically typed language which allows you to declare a variable without a type, for which it can conclude the type based on usage (and in Go, that’s just assigning the RHS’s type to the LHS in assignment declarations) is inferring the type of the variable it is being assigned to. I’d really like to see a citation for the definition you’re using, because it’s shocking to me that it would explicitly exclude this obviously trivial case.

                                  1. 4

                                    How is it the same mechanism? 3:int + 3:int is obviously different than x:?? := 3:int, even if the ?? is trivial to deduce.

                                    (3:int + 3:int) : ?? <===> x:?? = 3:int

                                    for which it can conclude the type based on usage (and in Go, that’s just assigning the RHS’s type to the LHS in assignment declarations) is inferring the type of the variable it is being assigned to.

                                    Go is not inferring the type based on usage in the program. Here is a trivial example:

                                    a := 1
                                    b := 1.5
                                    a + b
                                    

                                    This errors out with “invalid operation: a + b (mismatched types int and float64)”, yet 1 + 1.5 type checks. If Go was able to determine a variable’s type by its uses, it would be able to tell that a could be safely assigned the type float64. The reason Go doesn’t do this is because in the presence of mutable variables it would require… type inference.

                                    I’d really like to see a citation for the definition you’re using, because it’s shocking to me that it would explicitly exclude this obviously trivial case.

                                    I agree that inferring type based on usage is part of type inference, I disagree that Go is doing this. I mean as a quick sanity check, there are no uses of the variable in the RHS of an assignment!

                                    1. 3

                                      Can you actually provide a definition of type inference that excludes the sort of inference found in Go? I feel like, “type inference must include some non-trivial constraint solving component” as proffered by @pyon isn’t good enough. What does “non-trivial” mean?

                                      Interestingly, the Wikipedia article on type inference includes Go in its list of languages that does type inference. I feel like that article is poorly cited though. If you go to the talk page, there is a section with criticism that seems to argue your point. They do propose a definition and contrast it with something called type deduction:

                                      Type inference is a system whereby the types of variables, expressions and functions are computed from their use. A restricted form of this, where types of expressions are calculated from their subterms, it called type deduction.

                                      I think I might buy that, but there’s clearly some general confusion on the topic.

                                      Some other interesting remarks from Wikipedia:

                                      I think I must add the interesting note that in fact C++ can do type inference, not quite the kind you’d expect. C++ can infer the type of function template type parameters from function arguments. This calculation requires unification, and is not S-attributed, so technically it qualifies as type inference (despite the fact the Standard actually describes this as deduction :)

                                      and

                                      As a result of the wrong definition in this article a lot of languages are claiming to have inferencing type systems, including Rust and Go, which cannot do type inference.

                                      Interestingly, the comment about Rust is in fact wrong. Example. However, this comment was made in 2013, so I’m not sure whether it was true then or not. Note that Rust doesn’t have the same kind of type inference as Haskell, since the types of top-level functions are never inferred. I’ve heard people refer to this as “local type inference” or “partial type inference.”

                                      cc @apg

                                      1. 4

                                        Note that Rust doesn’t have the same kind of type inference as Haskell, since the types of top-level functions are never inferred. I’ve heard people refer to this as “local type inference” or “partial type inference.”

                                        IIRC, this was an intentional design decision, because global type inference can result in very surprising types, and any change can impact the type of variables far away from it. Rust certainly could perform global H-M inference, but forcing language users to occasionally specify what they mean ensures their assumptions and the compiler’s inferences are in line, and makes it possible for casual readers to figure out what type a given expression is.

                                        (Usually. It’s certainly part of my toolkit to declare a variable as unit type so the compiler will tell me what type it thinks it is.)

                                        1. 2

                                          It was indeed intentional. I love it. And whenever I write Haskell, I always write type declarations on top-level functions. Sometimes even on helper functions as well, if it helps readability.

                                        2. 3

                                          The consensus definition of people who study type systems and develop new inference algorithms is “not type checking”, where type checking is defined by your first quote. Determining a type from the types of its subexpressions is “checking”, “synthesis”, “deduction”, etc. If you admit checking as inference, then every language performs inference, and we’ve lost a meaningful way of discussing and classifying programming languages. In bidirectional type systems (local type inference), this distinction is explicitly coded in the presentation of the type system.

                                          I don’t know who writes Wikipedia articles, they probably list Go as having type inference because Go’s designers say it has type inference. Often people in our industry use terminology in a way that disagrees with how experts in the field use it. I mentioned Julia’s “dependent types” earlier but another good example would be saying that reference counting is not garbage collection.

                                          I agree that C++ performs type inference for template instantiation. Figuring out which types to instantiate a polymorphic type with is a classic type inference problem. This is why, for example, System F has explicit polymorphic instantiation in addition to annotations on function parameters–it does not admit type checking otherwise.

                                          1. 1

                                            I don’t know what the right answer to this is, but: would do you make of dynamic language compilers that use the phrase “type inference” to describe what they do? For example, SBCL (a Common Lisp compiler). The baseline is that you don’t know the type any expression has until you find out at runtime, and it’s perfectly conforming to the standard to not attempt to do any more. But it’s sometimes possible to determine statically that a given expression definitely have a certain type, and if you do so, you can optionally produce additional diagnostics and/or optimizations. SBCL calls this “type inference”, in the literal sense that it’s able to infer a type where it wasn’t explicitly specified. Although you could also call it other things, like, I don’t know, “incomplete type-checking”.

                                            1. 3

                                              I don’t know, maybe “best-effort type inference”? “Approximate”? “Heuristic”? After-market type systems for dynamic languages often heavily rely on non-local reasoning so I wouldn’t hesitate to call what they are doing “inference”, but it isn’t a type system in the traditional sense since it isn’t apart of the language definition. Honestly this sort of thing is outside of my area of expertise and I try not to post unless I’m sure of what I’m talking about, so forgive me for the non-answer

                                        3. 1

                                          (3:int + 3:int) : ?? <===> x:?? = 3:int

                                          Is it? My reasoning for thinking it’s not is based on the grammar. x:?? = 3:int is a statement, where as 3:int + 3:int is an expression. It’s not hard to imagine that they can/ and do have different rules for how the type checker operates (edit: on different syntactic constructs)… as seen by your trivial example.

                                          Go is not inferring the type based on usage in the program.

                                          You’re right! It’s not inferring the type due to its usage on the RHS. And while that is a property of more complicated, and more general, type inferencing algorithms, I fail to see how that is actually a requirement for it to be considered “type inference.”

                                          Consider your previous example: \x -> x + 1. Yes. It’s obviously the case that x’s type was deduced because of its use in that expression, but consider:

                                          y = \x -> x + 1
                                          z = y(2)
                                          

                                          z still ends up with the type int via inference, right?

                                          1. 2

                                            Is it? My reasoning for thinking it’s not is based on the grammar. x:?? = 3:int is a statement, where as 3:int + 3:int is an expression. It’s not hard to imagine that they can/ and do have different rules for how the type checker operates (edit: on different syntactic constructs)… as seen by your trivial example.

                                            The statement/expression distinction is a syntactical quirk that only matters to the parser, from the perspective of a type checker the only thing that matters is the set of typing dependencies a variable/expression/statement/whatever has.

                                            I’m afraid I’m going to have to bow out of this argument, I do not think I can convince you of my position, and as you said a few replies ago this thread has gone on too long.

                                            1. 2

                                              Thanks for going on this long! We agree (yay!) that whatever Go is doing, it’s not very advanced.

                                            2. 1

                                              Depends on the language. In Haskell, ghci tells me it ends up with type Num a => a.

                                              1. 1

                                                Duly noted, but the actual type is irrelevant. The fact that z is typed in a consistent manner is.

                                  2. 2

                                    The fact that we equate the Hindley-Milner algorithm to type inference is the problem here.

                                    Nobody is equating Hindley-Milner with type inference in general. But there has to be some nontrivial constraint solving component, for that’s precisely what distinguishes type inference from type checking.

                                    1. 3

                                      Nobody is equating Hindley-Milner with type inference in general.

                                      This is generally done, and I assumed based on your responses that you were doing so as well. My apologies.

                                      But there has to be some nontrivial constraint solving component,

                                      Why? This is what doesn’t make sense to me in your argument. Why, to call something “type inference” must there be some complicated constraint solving component?

                                      Does the compiler know the type of x based on what a user has entered? No. The user did not label it. How does the compiler figure out the type? It uses a simple rule that states, “the type of the LHS is equal to the type of the RHS”. This is the simplest deduction that could possibly be made, it is true, but that doesn’t mean that this simple deduction didn’t use logic.

                                      1. 1

                                        Why, to call something “type inference” must there be some complicated constraint solving component?

                                        It doesn’t have to be complicated. It just has to be nontrivial. For example, in Hindley-Milner type inference, constraint solving boils down to first-order unification (which has been well understood since, like, the 60’s) and an occurs check.

                                        It has to be nontrivial, because, otherwise, the type “inference” algorithm isn’t telling you something that isn’t obvious by induction on the program’s syntax.

                                        (Edit: Fix’d typo.)

                                        1. 1

                                          induction of the program’s syntax.

                                          I’m not sure why you’re using induction here. We’re not making a general statement, we’re making a concrete statement that the type of X is known because the type of T is known.

                                          1. 2

                                            The one making the general statement is the compiler writer: “This algorithm works on every valid syntax tree.” The proof is by induction.

                                2. 3

                                  it’s closer in spirit to Hindley-Milner

                                  It’s not. Hindley-Milner type inference is divided in two phases:

                                  • Constraint generation, via a recursive pass of the syntax tree, which is what all type checkers do.
                                  • Solving a system of type equations, which is what distinguishes type inference from mere type checking.

                                  In Go’s case, the type equations come out already solved, that is, in the form X = T, where X is a free type variable. So there’s nothing to solve, and hence, nothing to infer.

                                  1. 3

                                    There’s also nothing to infer in the C example, since the type of LHS is already known, and thus, there’s no free type variable at all!

                                    So there’s nothing to solve, and hence, nothing to infer.

                                    Define “solve.” “Solve for X, in X = T” may be a trivial problem, but it’s still solvable, and this is still a system of type equations, even if the cardinality (of those equations) is 1.

                                    1. 2

                                      I think @pyon is probably right that Go doesn’t do type inference, but I think the reasons cited are too far in the mud. I think the key to focus on is that inference requires some examination of the use of variables/expressions/functions. Go definitely doesn’t do that. See my other comment for more details: https://lobste.rs/c/dbma5a

                                      1. 1

                                        There’s also nothing to infer in the C example, since the type of LHS is already known, and thus, there’s no free type variable at all!

                                        Yep, and cmm wasn’t arguing that C has type inference.

                                        Define “solve.”

                                        “Solving” is using a nontrivial algorithm that turns a system of equations of the form T = T' into an equivalent system of equations of the form X = T, where Ts range over arbitrary expressions and Xs are free variables.

                                      2. 2

                                        It’s not. Hindley-Milner type inference is divided in two phases:

                                        Constraint generation, via a recursive pass of the syntax tree, which is what all type checkers do.
                                        Solving a system of type equations, which is what distinguishes type inference from mere type checking.
                                        

                                        And, for giggles, let’s take your definition.

                                        1. Constraint generation: You’ve said that all type checkers do this, which means that Go does this.
                                        2. Solving a system of type equations: You’ve also said that Go’s type equations come out to be X = T where X is a free type variable.

                                        Since X = T is an equation, and a system of equations is 1 or more equations, by your very definition of Hindley-Milner type inference, Go uses Hindley-Milner type inference.

                                        1. 1

                                          No. Hindley-Milner type inference has additional properties:

                                          • It doesn’t require the source program to come with any type annotations whatsoever. In particular, function arguments needn’t be annotated.
                                          • It assigns to every typable expression a principal type, which subsumes every other type it could be given.

                                          Edit: Re-read what I said: “Hindley-Milner type inference is divided in two phases.” I didn’t say anything that has those two phases is automatically Hindley-Milner.

                                          1. 1

                                            Key words doesn’t require, but it may have type annotations.

                                            It assigns to every typable expression a principal type, which subsumes every other type it could be given.

                                            But isn’t this basically that “foo bar” is a string, and 3.14 is a float? I’ve never encountered a programming language which doesn’t do this. Even dynamic languages often have a notion that 3.14 is a number, and that “foo bar” is a string.


                                            Edit: I know you didn’t say these 2 things are the only thing that make it’s Hindley-Milner. I prefixed with “for giggles” since it was intended as a straw man.

                                            1. 1

                                              Key words doesn’t require, but it may have type annotations.

                                              Sure, but Go requires function arguments to have type annotations.

                                              But isn’t this basically that “foo bar” is a string, and 3.14 is a float? I’ve never encountered a programming language which doesn’t do this.

                                              What’s the principal type of nil?

                                              1. 1

                                                Sure, but Go requires function arguments to have type annotations.

                                                Fair. It immediately dismisses it as Hindley-Milner.

                                                What’s the principal type of nil?

                                                nil does complicate things, for sure. In Java, for instance, type of null is probably Object, which subsumes every type that could be given. In Go, this is less obvious.

                                                1. 2

                                                  In Java, for instance, type of null is probably Object

                                                  In Java, the type of null is very rarely Object, e.g. the following is totally legal:

                                                  public void takesString(String parameter);
                                                  …
                                                  takesString(null);
                                                  

                                                  while this will fail at compile time:

                                                  public void takesString(String parameter);
                                                  …
                                                  takesString(new Object());
                                                  

                                                  null has every nonprimitive type, which is (a) surprising in the extreme once you start thinking about the implications, and (b) the source of many of the problems with it.

                                                  1. 1

                                                    nil does complicate things, for sure. In Java, for instance, type of null is probably Object, which subsumes every type that could be given. In Go, this is less obvious.

                                                    Errr, there are two different notions of subsumption at play here.

                                                    • A more general type schema (containing more quantified type variables) subsumes a more specific one (containing less quantified type variables).
                                                    • A supertype subsumes a subtype.

                                                    Hindley-Milner cares about the former, not the latter.

                                                    1. 1

                                                      A more general type schema (containing more quantified type variables) subsumes a more specific one (containing less quantified type variables).

                                                      like, int instead of Number?

                                                      1. 1

                                                        There are no quantified type variables in either int or Number.

                                                2. 1

                                                  It took me two minutes to write this response, then it took me almost two hours to finally decide to click “Post”.

                                                  I prefixed with “for giggles” since it was intended as a straw man.

                                                  I assume good faith in technical discussion. Hypothetical scenarios of the form “let’s assume X works this way, then blah blah blah…” are legitimate, but deliberately mischaracterizing the other party’s position in a conversation (“let’s assume you said X works this way, then blah blah blah…”) is… well… questionable.

                                                  1. 3

                                                    is… well… questionable.

                                                    I’m sorry.

                                                    Of note, my original argument was that it was in the spirit of Hindley-Milner, e.g. similar. Those two properties seem to fit with that. My assertion that you were defining Hindley-Milner by those two properties, was indeed wrong, and unfortunate.

                                    2. 7

                                      Ugh, I added a new disclaimer to the top of the article I’m linked to from this list (update 2 on http://www.jtolds.com/writing/2016/03/go-channels-are-bad-and-you-should-feel-bad/)

                                      1. 7

                                        I think we can all agree that this article is worse than Go is.

                                        1. 4

                                          I don’t like the idea behind this repository, it is only a list of complaints of which a lot are outdated, e.g. no decent IDE because in the mean time we got gogland from jetbrains. Also, a lot of the complaints are biased or non-objective like C-style syntax, no OOP or complaints about the error handling (which I personally like).

                                          More interesting would be a discussion of those complaints (and insults, ^F stupid, poor) because a lot of them are non-issues or look like simple rants. Things I can confirm are that handling the GOPATH is sometimes cumbersome and error prone, there is no dependency manager and code generation is no real alternative for generics.

                                          1. 6

                                            non-objective like […] complaints about the error handling

                                            The criticism of error handling goes something like this: 1) you can forget to check for err, in which case your program has a silent bug; 2) there is no way to abstract over the if err != nil { return err } pattern because Go does not have parametric polymorphism or variant types. What is non-objective about that?

                                            1. 3

                                              I think parent means objective to mean something provable in an obvious way. I was differentiating these in a similar fashion to filter out anything not meeting that standard. Examples of objective ones are whether it has OOP constructs, generics, or type inference. It will or it won’t. Error handling is more subjective given there’s still debate on which method is best to use & in what use cases. So, a person saying it’s problematic due to lacking/having (method here) is naturally making a controversial statement. Quite a few things on the list like that.

                                              1. 4

                                                There’s nothing controversial about the fact Go’s error-handling is more error prone than using a sum type and pattern matching. (Edit: What might be up to debate is whether it matters.) With Go-style error-handling, you can do nonsensical things like:

                                                • Make the result and error values both nil.
                                                • Make the result and error values both non-nil.
                                                • Attempt to use the result value when it’s nil.
                                                • Attempt to use the error value when it’s nil.

                                                With pattern-matching, you’d have to go out of your way to make these nonsensical things possible in the first place.

                                                1. 2

                                                  Make the result and error values both non-nil.

                                                  No problem with this, it’s a very common thing, e.g. short read that failed, but returned data. Or a short write that failed, but wrote some data.

                                                  1. 3

                                                    Okay, in that case, and only that case, you use a type that allows a partial result to be returned along with a warning.

                                                  2. 2

                                                    Now that’s a more objective take on the error handling issue. The debate is still whether it matters, could be mitigated by tooling analyzing programs, and so on. I’m not involved in that debate so I can’t say anything past I see people arguing over it.

                                                    1. 3

                                                      Of course, any issues created by language defects can be mitigated with external analysis tools. But using such tools complicates the programmer’s workflow, and makes it difficult to evolve the program in the face of future requirement changes, which must be modeled twice: once in an inexpressive type system, and then once again in a more expressive external tool.

                                                    2. 2

                                                      It’s a big claim to say “There’s nothing controversial about…” when clearly a lot of people disagree with you. Which inherently makes it controversial.

                                                      1. 2

                                                        The nonsensical things I listed in my original comment are unquestionably all possible with Go-style error handling, and all impossible if you use the sum type:

                                                        type outcome =
                                                          | Success of success_info
                                                          | Failure of failure_info
                                                        

                                                        And exhaustive pattern matching:

                                                        match potentially_failing_operation (ar, gu, ments) with
                                                          | Success (some_success_info) -> ... (* some_failure_info doesn't exist here *)
                                                          | Failure (some_failure_info) -> ... (* some_success_info doesn't exist here *)
                                                        

                                                        It’s an uncontroversial technical fact.

                                                        1. 3

                                                          This is missing the forest for the trees. The controversial part isn’t whether sum types are provably less error prone or not. The “controversial” part is whether it matters. You said it yourself:

                                                          (Edit: What might be up to debate is whether it matters.)

                                                          Focusing on the technical aspects when the controversy itself is more subjective seems strange to me.

                                                          In the case of handling errors in Go, I haven’t found that it matters that much. The convention is so ridiculously strong that it’s pretty hard to mess up.

                                                          Whether the absence of sum types at all matters or not is a different argument entirely. Most gophers I’ve come in contact with are content with the type switch over interfaces, but I think the lack of exhaustive case analysis is a huge bummer.

                                                          1. 1

                                                            Focusing on the technical aspects when the controversy itself is more subjective seems strange to me.

                                                            I prefer not to discuss matters of opinion, even if I do a very poor job of hiding my opinions, because I wouldn’t want to impose them on anyone else. Back to facts, the lack of automated exhaustive case analysis isn’t the end of the world, of course.

                                                            In the case of handling errors in Go, I haven’t found that it matters that much. The convention is so ridiculously strong that it’s pretty hard to mess up.

                                                            Whether the absence of sum types at all matters or not is a different argument entirely.

                                                            I think it depends to a large extent on your programming style. Some people first say “my program will have such and such subroutines”, and then implement them, without further questioning their original design. If you work in this fashion, then mechanically unenforced but socially established conventions are probably good enough.

                                                            However, I tend to work in a different way. As I become familiar with the problem at hand, I come up with insights that help me organize my code better, e.g. I find a key invariant of a tricky helper routine that would have been impossible to discover during the initial phases of a top-down design. These insights tend to trigger Major Refactoring Events™, and it would be totally crazy not to have a powerful type system making sure that the (often very finely grained) pieces that make up my program fit well together.

                                                            1. 2

                                                              I prefer not to discuss matters of opinion, even if I do a very poor job of hiding my opinions, because I wouldn’t want to impose them on anyone else.

                                                              Sure… But your framing mischaracterizes the controversy at hand. Focusing only on technical facts when the controversy is, in large part, subjective or experience based is really misleading IMO.

                                                              However, I tend to work in a different way. As I become familiar with the problem at hand, I come up with insights that help me organize my code better, e.g. I find a key invariant of a tricky helper routine that would have been impossible to discover during the initial phases of a top-down design. These insights tend to trigger Major Refactoring Events™, and it would be totally crazy not to have a powerful type system making sure that the (often very finely grained) pieces that make up my program fit well together.

                                                              I work the same way. This is why I think the lack of sum types in Go is a different argument than “errors don’t use sum types.” At some point, you have to look at what your actual experience is. I stumble on lack of exhaustive case analysis a lot more than I stumble on errors being typed with products instead of sums.

                                                          2. 2

                                                            One of goals was ultra-fast compilation. Does implementation of stuff like that cause significantly, slower compiles? Or no effect?

                                                            1. 4

                                                              My experience with implementing it in Myrddin says that it’s not a significant problem. The entire system compiles in about 5 to 7 seconds, with about 5% of the time spent in the type inference and generic specialization code, as far as I recall.

                                                              Having algebraic types that need to be fully specified would simplify that further.

                                                              1. 3

                                                                Algebraic data types in particular don’t have any effect on compile times. Other OCaml features (e.g. abstract signatures) are known to make type checking undecidable, and, in any case, I would recommend against using them on complexity grounds, even if they had no negative impact on type checking.

                                                              2. [Comment removed by author]

                                                                1. 0

                                                                  The technical fact by itself is uncontroversial. What’s up to debate is whether the technical fact constitutes a practical merit. Reasonable people can disagree over whether enforced exhaustive case analysis is a particularly useful feature - that’s a matter of opinion, of course. But there can be no disagreement over whether sum types make it possible to mechanically verify the exhaustiveness of certain case analyses - this is a technical fact, backed with proof.

                                                      2. 3

                                                        Also, a lot of the complaints are biased or non-objective like C-style syntax, no OOP or complaints about the error handling (which I personally like).

                                                        All criticism is subjective value-judgement attached to objective fact. Complaints that Go has C-style syntax, no OOP, or repetive error-handling are no less valid, no more “biased”, than liking those things about it. If we accept that those descriptors accurately apply to Go then considering them pros or cons are both equally valid subjective positions that reasonable people will disagree on.

                                                        There is no such animal as an “objective complaint”. Bias does not mean “a preference you disagree with”.

                                                        1. 1

                                                          Fork it and update? ;)

                                                          1. 1

                                                            Sure, but unfortunately there are only 24h in each day.

                                                        2. 4

                                                          Interesting that the C-like syntax is on every con list.

                                                          I never found the syntax to be a minus, but I do find the level of abstraction the language forces you to work at to be less than comfortable.

                                                          I’ll note this should be seen as more an indictment of my own lacks as a programmer than problems with the language.

                                                          1. 1

                                                            I think Swift is going to replace it. They’re moving outward from pure Apple stuff to Linux/Docker/server etc. ARC is also amazing. Reference: https://twitter.com/wilshipley/status/823332875298742272

                                                            1. 1

                                                              I don’t see that happening. Not for at least 15 years. Go and Swift are almost polar opposites in terms of ecosystem focus(servers vs client side), and the swift compiler is still in flux enough that I hear a lot about syntax highlighting being unstable, where go’s complier and tooling story is rather strong in spite of no IDE being dedicated to it. Go also has some heavy benefits, like having its own TLS stack, and being one of the forst http2 implementations. Swift definitly has upsides in the generics and typesaftey department. I don’t see it replacing Go however.

                                                            2. 1

                                                              I think this is missing the ‘rant’ tag