Go Enums Still Suck

(zarl.dev)

80 points | by el_hacker 30 days ago

22 comments

  • sethammons 29 days ago
    I see it a lot: there is a type of developer who loves brevity in writing of the code. It is critical to them. They believe that productivity is hurt with more verbose code. They optimize for writing.

    Depending on the project, maybe that is ok. I work on projects with multiple teams and zounds of developers. In this sphere, productivity is NOT increased with these kinds of things as they save moments during writing but cost more during onboarding and reading. The more you have to keep in your head to make the code make sense, the harder it is for new team members to spin up.

    Productivity in the kinds of orgs I work in is improved when individuals can look at a small section of code and understand it directly. If you are going up call chains to understand things, you are costing productivity. When you have to slow down and mentally parse something, you are costing productivity.

    I would take a hand written and hand maintained version of what this utility has as output over the utility every single time.

    • ikari_pl 29 days ago
      The more code I need to READ to understand what I'm looking at, the less optimized for maintenance it is. If it takes 5 lines to understand that the map I'm looking at is effectively used as a set, it's worse than if I had a Set abstraction.

      Of course now I do, 20 of them, because when there's no built in, there will be packages made by the community.

      • sethammons 29 days ago
        Congrats, you just inherited this code and have to fix it:

            Mercury 0.378,2439.7,3.3e23,57910000,88,0.0000000001,0,false
        
        There is no way to infer what any of the values are. They are practically magic numbers. How do you know if you swapped a pair of numbers there and what that will do to the application? You have to mentally map all those things.

        It is vastly, overwhelmingly more new-dev friendly to use:

            MERCURY: Planet{
          planet:              mercury,
          Gravity:             0.378,
          RadiusKm:            2439.7,
          MassKg:              3.3e23,
          OrbitKm:             57910000,
          OrbitDays:           88,
          SurfacePressureBars: 0.0000000001,
          Moons:               0,
          Rings:               false,
         },
        • Debilski 29 days ago
          I’m wondering why we don’t use any type and definition-aware IDEs that could render the first line as a proper table (especially once there is a list of items of the same type involved). With headers and aligned fields. Similar to how Word would do it with inline tables.

          Ideally of course with a switch to change to the second, more verbose definition.

        • zeven7 29 days ago
          Maybe this data should be stored in a CSV and edited in Google Sheets/Excel.
          • hbogert 27 days ago
            I've got a job for you, mine at my previous company!
        • zarldev 29 days ago
          the values are specified in the definition of the iota type.

          ``` type planet int // Gravity[float64],RadiusKm[float64],MassKg[float64],OrbitKm[float64],OrbitDays[float64],SurfacePressureBars[float64],Moons[int],Rings[bool] ```

          • sethammons 29 days ago
            yeah, and now, mentally count how many values you are in, don't lose count now! Why offload that to your brain where you could miscount instead of using structured data?
            • rat9988 29 days ago
              Because your whole argument is misconstructed. There are cases where longer code is better, but there are many cases where longer is worse too. Because the tools the language provide, on top of being more succint, make the intent more obvious.
      • deelly 29 days ago
        Huh, for me its opposite, I`m ok with reading/fixing/updating few pages of code if its easy to read and easy to update.

        Short code with multiple levels of abstractions, with dynamic inheritance, and with a lot of dependency inversion, etc. etc... I don't like it. I will spend a lot more time (probably) just digging into code while trying to understand it.

    • vsupalov 29 days ago
      That's the reason why I chose to focus on Go instead of Rust after immersing myself in it for a while.

      Rust invites you to aim for brevity and crafting smart solutions whenever you can. Don't get me wrong, sometimes this leads to beautiful code. It can be super satisfying to write. But there is only so much fun in reading yet another 10-step iterator method chain which works due some arcane edge case in the middle of it.

      I vastly prefer reading dumb and obvious Go code. It's just easier to understand and to work with.

      • johnisgood 29 days ago
        This is one of the reasons for why I prefer C over Rust for reference implementations. I look at the code and I understand it much better than the Rust version.
        • packetlost 29 days ago
          I think I agree with this. C is, if nothing else, very simple to read and understand (generally). For reference code, it seems like a good choice. I think you need a stronger argument to use C over any other language for something that is meant to be bullet proof or run in production though.
    • grey-area 29 days ago
      But if you want brevity you could just use types!

      https://go.dev/play/p/uUNHxM8zqY9

      Not sure I understand what this adds over a list of plain old types.

      • sethammons 29 days ago
        This is the right answer - there should be a planet type.
    • fireflash38 29 days ago
      The only caveat to that is: if it's a pattern that is used frequently. For example, the tons of auto-generated gRPC code. First go around, sucks. You have to dive & read a lot. 2nd-3rd go around, you might remember more bits of it so you can use it with only a few lookups of defs. More than that? You likely don't need to dive into it at all to understand.

      So perhaps this tool would be very useful, if you're going to use it a ton. Or use it only once and not really have to access the internals. But if it's something that is bespoke, and you update it infrequently, that's ripe for frustration on re-reads.

    • couchand 29 days ago
      If you're working in an environment that categorically requires all high-level concepts (say, iterating over a collection or representing mutually-exclusive data) to be encoded using low-level mechanics, your productivity will suffer.

      But don't be confused: this is not the lost productivity of a missed optimization. We're enabling orders of magnitude of change by giving the developers tools for thinking rather than a mere code to grunt at the machine.

    • api 29 days ago
      “Go is a language for people who like to ship more than they like to code.”

      Low cognitive load is the biggest thing Go got right. That being said it doesn’t get it perfectly right and there are a few places it overdoes it.

    • mananaysiempre 29 days ago
      I like short code, but for exactly the opposite reasons. I can write long stretches of code; I won’t be very happy doing it, but all it takes is to start and keep going.

      Reading vast plains of mostly-obvious code, though, attempting to extract the author’s insights from them—that can be genuinely difficult. It’s also very taxing, because it’s never going to be all obvious, you see? Only mostly so. At some point somebody, somewhere, needed to tweak something just a bit to avoid a momentary difficulty, or their brain slipped, or they were new and didn’t know a subtle detail of how things are supposed to be done. Whatever the reason, I now need to remain in a state of constant vigilance for the entire journey.

      Give me two pages of code to stare at for an hour until they click any day. As funny as this sounds, that is just plain less effort (and less perception of wasted effort as well, if we’re being honest).

      (Now, I wouldn’t object to having both options—a concise description and its long and borung expansion for when I’m just not smart enough to get it—but that’s a language design problem that has for the most part remained unsolved for several decades now. So I’m not holding my breath.)

    • bananapub 29 days ago
      this sort of comment is extremely common from people using languages that don't allow for abstractions that reduce LOC, like Go and Python.

      cause or effect? who knows.

      • sethammons 29 days ago
        Maybe I am naive. Help me out. My familiar languages are Go, Elixir, Python, Perl, Ruby, and some Javascript. I would have said all of these languages allow for abstractions that reduce lines of code.

        What I don't want is abstractions that increase cognitive load.

        The case presented in the article requires many mixed types in a list. The longer the list, the more you have to keep in your head. In Go, it is common to use table driven tests and you can get something like:

            cases := struct{
              wins int
              losses int
              is_eligible bool
            }{
              {23, 13, true},
              {34, 1, true},
              {3, 0, false},
            }
        
        Each case is abstracted and minimal. But it is very clear as you read that what each is because there are few items in the list. If it was a dozen properties, like in the post, you visually cannot parse that as fast. When the list of properties gets long, you are now mentally counting "ok, the 9th parameter is a float, oh, hm, the 9th param is showing an int... oh, I forgot the 7th param."

        Abstractions should be clear and make understanding easier. With so many parameters, labeling them makes them clear. Abstractions are not just for reducing line count.

        Where am I going wrong? What is the better alternative?

  • jjice 29 days ago
    I went years without using a language with sum types and was fine. As soon as I was exposed to them, I was hooked. They just make so much sense in so many places, especially in trees.

    It seems that over the past five or so years, they've been adopted more as pattern matching has. If they're not an explicitly feature of the language, then they can be replicated (like with Java sealed classes).

    I'm not sure if Go would ever implement them, or add something that we can more easily replicate sum types with, but god damn are they my favorite language feature that so few languages properly support.

    I wouldn't have expected Go to implement them, but after generics and range over functions, I feel like anything could be game.

    • pjmlp 29 days ago
      Go doesn't need to go full speed on sum types, plain basic Pascal enumerations from 1970 instead of that hacky iota/const dance, would be a huge improvement already.
      • randomdata 29 days ago
        There is no practical difference between enums in Pascal and Go.

        What is different is that Pascal added an additional layer over enumerations – what is basically a poor man's sum types – to hide that there are enums under the hood. In fact, one has to perform an explicit conversion if they want to access the enum. Sum types predate enums, and no doubt they were trying to stay close to sum type semantics without the overhead. Something later languages, like C and Go, chose to forego in favour of exposing the enums naturally.

        But if you're going to go to all the trouble of implementing a poor man's sum types, why not just do it properly? It's not 1970 anymore. We've solved the overhead problems. Sum types are perfectly viable these days.

        • pjmlp 29 days ago
          Of course not, first Go would need to actually have support for proper enumerations.

          Go neither has the 1970 Pascal enumerations, nor the 1976 ML sum types.

          Both approaches too advanced to implement on 2009, apparently.

          We don't want the poor minds Go is targeted at, as per Rob Pike's words, to have imposter syndrome learning the language.

          As it is, is a non starter to even bother.

          • randomdata 29 days ago
            It is Pascal that does not truly support enums, if such a thing can be said. It has them, technically, but the language is not designed to meaningfully make use of them. The poor man's sum types abstraction works to hide their existence.

            While sum types first appeared in Algol 68, there were some unsolved issues at the time. So Pascal came along with its half-assed 'sum types' as a tradeoff between the benefits of true sum types and what was tenable given the constraints of the time. But there is no reason for any language created in the last 50 years to follow in that path. We've long solved the problems they had.

            Go benefits from enums because it has a very basic type system.

            Trying to improve on enums by extending the type system, along the lines of what Pascal did or otherwise, is hilariously nonsensical. If you are going to build a better type system, just get rid of enums completely. You don't need them.

            Sure, it was a reasonable compromise in 1970, but it is not 1970 anymore.

            • pjmlp 29 days ago
              Go still hasn't catched up with either 1970 nor 1976, no matter how many words you happen to write.

              An idiotic hack, remains an idiotic hack.

              • randomdata 28 days ago
                Are you expecting this straw man that you keep wanting to talk with to come to life and follow you down a yellow brick road, or what?

                Yes, enums are a hack, forever and always. Nobody disputes this.

          • panzi 27 days ago
            As I understand it, Go doesn't even have what you get with C enums+certain compiler flags.
            • randomdata 26 days ago
              Nobody thinks that Go has every feature under the sun. Indeed, it lacks features that even C has.

              What it does not lack, however, is enumerations. Enumerations establish the number of something. Go has that feature. That's all enums are. Nothing more, nothing less. Other languages have additional features, like value constraints, that can enhance the use of enums, but those are entirely different features.

              The idea that Go doesn't enums because it lacks those other features is like saying that C doesn't have loops because it doesn't have the equivalent of Go's range operator. How ridiculous. Obviously C has loops. Anything else that might enhance those loops is a separate feature.

              • panzi 25 days ago
                Then let me rephrase: Go lacks the kind of features around enums that I personally would expect every new statically typed language that was developed in the last quarter of a century should have (if it has enums at all).
    • rightbyte 29 days ago
      A nullable pointer is a sum type and Go has them.
  • pjmlp 29 days ago
    Meanwhile in the 1970's....

        type
            Planet = (Unknown, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune);
            PlanetContainer = record
                gravity: real;
                radiusKm: real;
                massKg: real;
                orbitKm: longint;
                orbitDays: integer;
                surfacePressureBars: real;
                moons: integer;
                rings:  boolean
            end;
    
        var
            planets : array [Planet] of PlanetContainer;
    • nurettin 29 days ago
      and

          const PlanetIndex = 1..9;
          Innersolarsystem = Mercury..Earth;
  • baby 29 days ago
    Golang doesn’t have sum types is Golang’s biggest problem, not generics
    • klabb3 29 days ago
      Yup. I love Go but they didn’t get types right. The level of abstraction is too low, and embedding and iota never feel quite right.

      Rust got the basics right. Pattern matching, no nulls, traits, sum types, and the sugar around try with the ?-notation is also great.

      Despite it all, this is not a dealbreaker, and Go shines for striking a great balance of a sensical language. But I always cringe a little inside when fanboys defend even the most awkward and annoying aspects of Go.

      • dist1ll 29 days ago
        > embedding [..] never feel quite right.

        funnily enough, I would argue that struct embedding is one of the things that Go actually got right. It's simple and elegant, and I think it could've been a useful addition to Rust.

        • klabb3 29 days ago
          Yeah to be fair I don’t think I even have a problem with embedding per se. It’s quite nice. It’s more the fact that embedding can be used to work around missing type system features. I can’t recall exactly, but I remember using embedding for things that it probably wasn’t designed for, because it would save me a ton of boilerplate.
        • dontlaugh 29 days ago
          Almost every single case of embedding I've encountered was buggy in some way.

          Explicit composition is much more reliable.

          • lamontcg 29 days ago
            If someone autogenerates one way or another all the delgation, then that's just as buggy as embedding. The presence of all the explicit composition in the codebase doesn't mean that anyone actually sat down and thought about it all.
            • dontlaugh 29 days ago
              Autogenerating delegation is bad for the same reason. Having to explicitly delegate in every single case forces you to think about it.

              Alternatively, it forces you to use the type system to make a better abstraction.

              • lamontcg 29 days ago
                > forces you to think about it

                forcing people to type something never forces them to think about it.

      • pjmlp 29 days ago
        As someone that also has to wear DevOps hat, I really would like that Rust had matured first.

        At least newer CNCF projects aren't going into Go as much as they previously did.

    • GaryNumanVevo 29 days ago
      Also a reason why Golang isn't viable for DS/ML. I have 6 years of experience in Go, and I thought it would be easy to implement a PyTorch like library in a weekend, I had done so with Python and Julia previously. Go's lack of sum types is a huge restriction, once you hit this wall you realize it's the reason for a lot of wacky workaround in various libraries (especially any parsing libraries!)
  • kubb 29 days ago
    It would be a very significant improvement to have tagged unions in the language.
    • dgb23 29 days ago
      When I played around with zig, I got a much better understanding what they actually are and how they are (or can be) represented in memory. It's a language that lets you actually feel what your program is doing.
    • otteromkram 29 days ago
      You didn't seem to be criticizing C#, though?
  • Sharlin 29 days ago

      type planet int // Gravity[float64],RadiusKm[float64],MassKg[float64], ...
    
    Sorry, but what? Is this really an ad-hoc DSL embedded in a regular comment that's then preprocessed by… something, to generate the actual Go code? Certainly there's something that sucks here!

    Edit: I'd find it marginally less sucky if there at least were some special prefix like `//#` or `//my-preprocessor` or whatever to mark magical semantically significant comments. And what's the deal with the `field[type]` syntax that seems totally ad hoc, why not use the standard Go syntax `field type`?

    • grey-area 29 days ago
      Horrible isn't it.

      Not sure why they don't just use a struct with some fields, then just create instances of those instead of magic comments and a preprocessor.

      https://go.dev/play/p/uUNHxM8zqY9

      Mercury = {"Mercury",190.0,...}

      It would not be any more verbose and would let them define constants for use elsewhere if they wish. If they want to control values for validity they can use a NewPlanet func.

      I do agree go's enums suck but they need to take a step back and look at what they've created and what would be an easier way to create this if they really need it. It seems like they want real types here, not an enum and if they do want constant planet types it's very easy to make them without all this busywork.

      • randomdata 29 days ago
        > I do agree go's enums suck

        There's not much more you can do with them. A enumeration is literally assigning a number to something. That is all. Enums suck, fundamentally.

        Other language features, like value constraints, can help improve the experience of using enums, but, really, if we are inventing a language, why have enums at all? If you think you have a use case for enums, a more advanced type system will offer a better model for your problem.

        Sure, enums were a clever hack in the days of yore when we didn't have a good grasp of type theory, but are pointless in a modern language. Of course, Go is purposefully trying to be a language of yore, so it stands to reason that it would have them and all the suckiness that goes along with it, but, again, if we want it to be a modern language then you don't need to hack on top of enums with new features to make them nicer. You can get rid of them entirely.

      • hombre_fatal 29 days ago
        They clearly want an enum/sum type which an array of structs (which is the trivial solution they started at) doesn't provide.

        You're seeing someone try to address their issues with X and suggesting "here's how you use X." It doesn't track.

        • grey-area 29 days ago
          Nope, I'm saying don't use X at all, because enums are not the right thing to use to solve this problem. This solution of magic comments and a precompiler is worse than not using enums at all.
    • TheDong 29 days ago
      That's idiomatic go. See https://go.dev/blog/generate and '//go:generate stringer -type=Pill'

      See also '//go:embed', which also turns a variable into a magic type via an ad-hoc comment-only DSL.

      In go, it is idiomatic to say "macros slow down compilation and often require a second pass, go compilation is fast, macros are bad", and then also to have an extra "make sure go generate is up to date" CI step which parses your go codebase 10 more times, forks dozens of processes, and isn't type-safe since of course it's not it's literally a comment you can typo "//gog:enerate" and no one will notice.

      • grey-area 29 days ago
        Worth noting that most go codebases don't contain magic comments like this.

        I've certainly never used them and agree they are horrible in every way.

      • joneholland 29 days ago
        I frequently tell people that Go is my least favorite language for reasons like this and the response is often some form of the no true Scotsman fallacy.
    • logicprog 29 days ago
      Having to make ad hoc domain specific languages embedded in your comments that are then processed and expanded by entirely separate compilers, essentially reinventing the C macro preprocessor, is in my opinion the mark of a terminally underpowered language that even the people regularly using it realize is underpowered even if they don't admit it to themselves.
    • UncleEntity 29 days ago
      I don't understand what's wrong with using an actual DSL?

      I have this ASDL generator I use for all my grammar parsing experiments (to build the AST to parse into) that it isn't all that complicated -- mostly because it has zero error checking -- and, if I understand TFA correctly, could be adapted to what they are attempting to do in about an hour. And most of the complexity comes from using mustache as the template language and having to read their docs every single time I want to change something.

      True, it does add an extra build step and the reason it's 'simple' is because I've rewritten it at least three times (but that's mostly to learn how a new (to me) parser generator works) but it's now at the point where I can add new AST nodes in conjunction with filling out the grammar rules without having to really think about it. Oh, and I do kind of enjoy shaving yaks so there's that.

    • xienze 29 days ago
      There’s a history of this sort of faked annotation thing in Go. The most notable example being comments used to denote how a struct’s fields are mapped to/from JSON.
      • kelnos 29 days ago
        The struct field tags thing (which are not comments) is at least something built into the language, that other code can access via reflection. Not being able to do this for this use-case, and having to rely on comments that are preprocessed by a code generator, is just a shame.
        • zarldev 29 days ago
          Yeah extending the struct tags to iota definitions would have made things much better.
        • vsnf 29 days ago
          Maybe it's slightly better than total shit, but it's still pretty shit. Half the struct field tags aren't even typechecked, and will force you to go on hours long goose chases wondering why something isn't working only for you to realize eventually that 'someField string `db_orm_field_1:"0"' is actually supposed to be '"someField string `db_orm_field1:"0"'.
          • neonsunset 29 days ago
            Something like this just doesn't happen in C#. Neither in runtime reflection based ORMs nor in code generation based (micro)-ORMs (which, unlike Go, does not need extremely clunky manual scripting and "just works").
    • axitanull 29 days ago
      I believe back in the Java days, there are packages that are also doing something like this (comment preprocessor for generating code)

      Those plugins would try to point out that the comments that they would preprocessed would look somewhat different from regular comment, e.g.

      //this is a regular comment

      //#while comments that start with '#' would be preprocessed.

      It's still an issue that would shoot the foot of the next person who would maintain your code though.

      • Sharlin 29 days ago
        Yes, I'd find it marginally less sucky if there at least were some special prefix like //# or whatever to mark magic semantically significant comments. And what's the deal with the `field[type]` syntax that seems totally ad hoc, why not use the standard Go syntax `field type`?
    • pjmlp 29 days ago
      That is the whole Go mindset right there.
    • kelnos 29 days ago
      Yes, I think it's actually a decent way of doing it, given how limited golang is when it comes to enums. (Better might be to use tags, but I think they are only allowed on struct fields.)

      I think codegen is generally fine for repetitive tasks. It's just a shame that golang doesn't give you a way to do this built-in. Even java has this feature, even if it's lacking in some regards.

  • ergonaught 29 days ago
    I see literally nothing in this post that elaborates on "Go Enums Still Suck".
    • axitanull 29 days ago
      The first sentence literally points out that the post is a continuation of previous post that elaborates on why Go Enum sucks.
      • Brian_K_White 29 days ago
        ...and then does not show in what way they still suck.

        Or was it just very dry and although the text did not say so explicitly, the examples did some how show go enums in the act of sucking?

  • perbu 30 days ago
    I'm wondering if it be better if an iota would assign random, unique numbers at compile time, just to make it clear that these are not entirely safe identifiers inbetween compilations.
    • datascienced 29 days ago
      If only there were a way in computer science to guarantee you couldn’t misuse a value. Then you wouldn’t need to rely on using UB to scare people into not making a mistake. (A dig at Go not your comment :-)
      • logicprog 29 days ago
        Yeah, if only there were decades of programming language design research and field testing since C for Go to draw on...
    • foldr 29 days ago
      Iota does have other uses where this behavior wouldn't work, such as for defining bitmasks:

          const (
             Flag1 = 1 << iota
             Flag2 = 1 << iota
             Flag3 = 1 << iota
             ...
          )
      
      The magic const shorthands even let you omit the explicit definitions for everything after Flag1, but I think it's easier to get the idea when all the definitions are explicit.
      • neonsunset 29 days ago
        Or, you know, it could learn a thing or two from C# enums (which are not even that good):

            [Flags]
            public enum Options
            {
                Default = 0,
                Option1 = 1, // can also be defined as 1 << 1
                Option2 = 2, // can also be defined as 1 << 2
                Option3 = 4 // can also be defined as 1 << 3
            }
        
        The above is a choice, and an enum can be defined normally if it's not a bitmask. .ToString() would even format it correctly if multiple flags are toggled, it can be easily parsed with Enum.Parse<MyEnum>(text) and more. And hell, this is just tolerable implementation. It makes a lot of sense given historical context of C enums, which it's a direct improvement over, but not a step away in what is now considered to be the right direction represented by Rust enums instead.

        (Luckily, unlike Go, C# allows to trivially define Result<T, E> class/struct and a method on it in the form of Map(ok => .., err => ...))

        • foldr 29 days ago
          I don't think my comment ought to be the trigger for a generic rant against Go enums (or lack thereof). I was just pointing out a motivation for iota to increment in sequence from zero (rather than assigning a random unique value, as OP suggested).
          • neonsunset 29 days ago
            You are right. Please don't take my criticism of a bad language to be a criticism of a perfectly reasonable comment :)
        • masklinn 29 days ago
          C# enums are strictly worse: they’re C enums, meaning they’re no safer than integers, but they don’t tell you about that.

          Go at least does not pretend it has anything like enums or sum types, and that is to its credit. You create a Go integer type, it’s flagrantly shit, but it’s not subtle about it.

          • neonsunset 29 days ago
            C# enums explicitly tell you that and it is well documented. You have easy Enum.IsDefined(enumValue) as well as Enum.GetName(enumValue) which work exactly as advertised. Switches on enum values will tell you to handle the default case too.

            They are not ideal by modern standards when you look at Rust. But to state that they are worse than Go's is a new and I can't find a way in which this kind of statement would defensible.

  • Sacro 29 days ago
    Go doesn't have enums.
    • randomdata 29 days ago
      It doesn't have what Rust mistakenly calls enums - what everyone else in the world calls sum types, but it most definitely has enums in the traditional sense. Enum normally refers simply to assigning a number to something. That is what iota does.
      • pjmlp 29 days ago
        Enums in the traditional sense, is what Pascal and C have been doing it for decades.

            (* Pascal in 1970 *)
            Planet = (Unknown, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune);
        
            // C in 1989
            enum Planet {Unknown, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune};
        
        Plus type system guarantees, and in Pascal's case, run time capabilities to query enum values and ranges.

        Go's hack has barely anything to do with that.

        • randomdata 29 days ago
          Yes, those examples too assign a number to something. Enumeration is not a new concept. In fact, it's an incredibly old concept that emerged in programming langauges as a hack to work around our, a the time, limited understanding of type systems. A modern type system has no reason for enums at all. Of course, Go purposefully tries to be an 'old' language, hence why it copied the antiquated pattern of providing assignment of numbers as a language feature.
          • pjmlp 29 days ago
            You mean it copied how programming used to be before 1970's. Great achievement.
            • randomdata 29 days ago
              Not really. Enums as a programming language construct are a 1970s invention.

              Funnily enough, Algol 68 had sum types, although controversially so due to overhead concerns. If only Go had copied the state of programming before 1970, then we wouldn't have to listen to all the nonsense about enums sucking. Of course they suck – they are, and always will be, a hack to work around a limited type system.

              • pjmlp 29 days ago
                Go does not have any kind of enumerations, what is has are constants and a gigantic hack for pseudo enumerations, they even compile with broken values!

                    package main
                
                    import "fmt"
                
                    type planet int
                
                    const (
                        unknown planet = iota // invalid
                        mercury               // Mercury 0.378,2439.7,3.3e23,57910000,88,0.0000000001,0,false
                        venus                 // Venus 0.907,6051.8,4.87e24,108200000,225,92,0,false
                        earth                 // Earth 1,6378.1,5.97e24,149600000,365,1,1,false
                        mars                  // Mars 0.377,3389.5,6.42e23,227900000,687,0.01,2,false
                        jupiter               // Jupiter 2.36,69911,1.90e27,778600000,4333,20,4,true
                        saturn                // Saturn 0.916,58232,5.68e26,1433500000,10759,1,7,true
                        uranus                // Uranus 0.889,25362,8.68e25,2872500000,30687,1.3,13,true
                        neptune               // Neptune 1.12,24622,1.02e26,4495100000,60190,1.5,2,true
                    )
                
                    func main() {
                        landingOn := venus
                        fmt.Printf("Landing on %d\n", landingOn)
                        landingOn = 42 // What a hack!
                        fmt.Printf("Landing on %d\n", landingOn)
                    }
                
                
                https://go.dev/play/p/9AzJeHB0YbT
                • randomdata 29 days ago
                  > they even compile with broken values!

                  Of course. Same as C:

                      #include <stdio.h>
                  
                      enum planet {
                          unknown,
                          mercury
                          /* ... */
                      };
                  
                      int main() {
                          planet landingOn = mercury;
                          printf("Landing on %d\n", landingOn);
                          landingOn = planet(42); // What a hack!
                          printf("Landing on %d\n", landingOn);
                      }
                  
                  Value constraints have nothing to do with enums. Enums are exclusively about numbering – and as your code shows, the numbering works just fine. Value constraints are an independent feature. One that Go (and C) lack completely, not just in the case of enum values. You can compile with broken values throughout. Consider:

                      type EmailAddress string
                      var email EmailAddress = "Not an email address" // A-OK!
                  
                  Compare that with a language with a more advanced type system like, say, Typescript:

                      type EmailAddress = `${string}@${string}`
                      let email: EmailAddress = 'Not an email address' // Compiler error
                  
                  You can probably make a good case that Go would benefit from value constraints, among other features, but if you're going to start adding such features, why have enums at all? Again, they're a hack to work around a limited type system. If your type system isn't limited...
                  • pjmlp 29 days ago
                    Now do the same with 1970's Pascal enumerations.
                    • randomdata 29 days ago
                      Pascal doesn't really have enums as a language feature, so there is no direct analog. It has enums as an implementation detail, but for all intents and purposes the language could be implemented with something other than enums. The same is not true of C or Go, which make enums a first-class citizen of the language.

                      That is, with the caveat that Pascal does allow the union to be converted to its underlying enum representation. If we provide such conversion to get at the enum, then sure:

                          Program Lander(output);
                          type
                            planet = (unknown, mercury);
                          var
                            landingOn: Integer;
                          begin
                            landingOn := Ord(mercury);
                            writeln('Landing on ', landingOn);
                            landingOn := 42;
                            writeln('Landing on ', landingOn);
                          end.
                      
                      But you're ultimately comparing apples and oranges.
                      • pjmlp 29 days ago
                        That program isn't correct, you are faking it by using an Integer.
                        • randomdata 28 days ago
                          Indeed. Just as I explained, Pascal doesn't really have language support for enums. The pseudo 'sum types' abstraction works to try and hide the fact that enums exist, only presenting the implementation detail enum if you explicitly convert a union element to its enumerated ordinal – which, as it happens, is an integer type.

                          The program is correct – it complies and runs just fine – but the original ask was faulty. Without any kind of real enum support in Pascal the language, there is no way to perform the same operations as is possible in languages that do have natural enum support. This program is the best you are going to do in the absence of that support. This was all detailed earlier.

                          Again, you are trying to compare apples and oranges. Rust[1] may try as it might to call sum types enums, but enums and sum types – along with Pascal's pseudo 'sum types' – are different models. I understand where your confusion stems from as there are a lot of similarities, but there is also a difference.

                          [1] And Swift originally, but its documentation is updated to make clear that, contrary to what the name implies, the language's enum construct is actually sum types and not enums.

                  • panzi 25 days ago
                    And without the explicit cast I'm getting:

                    GCC: error: invalid conversion from 'int' to 'planet'

                    clang: error: assigning to 'enum planet' from incompatible type 'int'

                    Yeah, with casts C let's you reinterpret anything as anything else. Similar to `as any` in TypeScipt.

  • 0xjnml 29 days ago
    Go does not have enum types and Go does not have [Pascal-style] subrange types.

    The correct title should read "Go named constants suck", but then I'm not sure what's wrong about them.

  • ptman 29 days ago
    How does this compare to enumer? https://github.com/dmarkham/enumer
  • Thaxll 29 days ago
    In most languages enum suck, it's not specific to Go, lot of pitfall and ABI issues, even my modern languages like C# have those.
    • randomdata 29 days ago
      Yes, enums fundamentally suck. There is nothing you can do in a language to make them not suck other than to get rid of them...

      ...which, indeed, is quite viable as enums are just a hack to work around a limited type system anyway.

  • cupofjoakim 29 days ago
    I'm having some real issues with legibility on this page. I'd like to encourage the author to decrease the font size, increase the line spacing and look into a smaller max-width for the paragraphs. It'd also be nice to have some spacing before a new sub-heading.

    I'm coming from a 4k screen, so that does play into it, but i honestly just could not read the article without changing the text properties here.

    • chimeracoder 29 days ago
      > I'm coming from a 4k screen, so that does play into it, but i honestly just could not read the article without changing the text properties here.

      It's not just using a 4K monitor. It's very difficult to read on mobile as well (and even harder to change properties there, short of reader mode).

  • Alifatisk 30 days ago
    What's up with the horizontal scroll?
  • coxley 29 days ago
    What enums?
  • benreesman 29 days ago
    I have a mountain of respect for Bell Labs and its contributions to the public welfare, and a lot of respect for the current group of alumni, mostly at Google, and mostly affiliated to a greater or lesser degree with golang. I have my differences with one or two of them (Pike telegraphs a wildly overcompensated imposter syndrome, but he’s almost as much of a genius as he acts like he is and who am I to judge on an overcompensated imposter syndrome, moreover when the guy in at the next desk over is Ken Thompson, who wouldn’t be a little intimidated by the legend).

    With that said, golang is too opinionated for its level of adoption, too out-of-touch with emerging consensus (and I’m being generous with “emerging” here, the Either monad is more than an emerging consensus around the right default for error handling), and too insular a leadership to be, in my personal opinion, a key contender outside some narrow niches.

    I’m aware that there are avid advocates for golang on HN, and that I’m liable to upset some of them by saying so, so I’m going to use some examples to illustrate my point and to illustrate that I’ve done my homework before being critical.

    Many, including myself, became aware of what is now called golang via this presentation at Google in 2007 (https://youtu.be/hB05UFqOtFA) introducing Newsqueak, a language Pike was pushing back in the mid-90s with what seems to be limited enthusiasm no greater than the enthusiasm for its predecessor Squeak. Any golang hacker will immediately recognize the language taking shape on the slides.

    I’ve been dabbling with golang for something like a decade now, because I really want to like it. But like a lot of the late labs stuff it seems to have suffered from the dangerous combination of the implications of Richard Gabriel’s Worse is Better observation: it was simpler, faster, cheaper, and ultimately more successful to incrementally adapt innovations from Plan9 into Linux (and other Unices), to adapt innovations from sam and acme into nvim/emacs (and now VSCode), and to adapt channel-based and other principled concurrency from Newsqueak/golang (not to mention Erlang and other more full-throated endorsements of that region of the design space) into now countless other languages ranging from things like TypeScript and Rust at the high end of adoption all the way to things like Haskell at more moderate levels of adoption. Ironically enough, the success of UTF-8 (a compromise for the non-ASCII world but the compromise that made it happen at all) is this same principle in action via the same folks!

    And golang would be fine as yet another interesting language serving as a testbed for more pragmatic applications of radical ideas: but it’s got corporate sponsorship that puts Sun Microsystems and Java to shame in scale and scope, but done quietly enough to not set off the same alarm bells.

    The best example of this is probably this GitHub issue: https://github.com/golang/go/issues/19991 (though there are countless like it). I’ve worked with Tony Arcieri, he’s brilliant and humble and hard-working and while we haven’t kept in touch, I keep an eye out, and he’s clearly passionate about the success of golang. But proposal after proposal for some variation of the Either monad has died on procedural grounds for nearly a decade, all while being about the only thing that everyone else agrees on in modern industrial PLT: TypeScript supports it, Rust supports it, C++ de-facto supports it via things like abseil and folly, and of course the hard-core functional community never even bothered with something worse in the modern era. You can even kind of do it, but there are intentional limitations in the way generics get handled across compilation units to ensure it never gets adopted as a community-driven initiative. Try if you don’t believe me (my golang code has a Result type via emacs lisp I wrote).

    Another example is the really weird compilation chain: countless serious people have weighed in here, I’ll elide all the classics because most people making these arguments have their own favorite language and they’ve all been on HN dozens of times, but a custom assembly language is a weird thing to have done, almost no one outside the hardcore golang community thinks it’s sane, the problems is creates for build systems and FFI and just everything about actually running the stuff are completely unnecessary: there are other IRs, not all of them are LLVM IR if you’ve got some beef with LLVM IR, and given that go doesn’t seriously target FFI as more than a weird black sheep (cgo) there’s, ya know, assembly language. It’s a parting shot from the Plan9 diehards with the industrial clout to make it stick.

    The garbage collection story is getting better but it’s an acknowledged handicap in a MxN threading model context, it’s not a secret or controversial even among the maintainers. See the famous “Two Knobs” talk.

    Raw pointers, sum types, dependency management, build, generics that never get there, FFI: solved problem after solved problem killed by pocket veto, explained away, minimized, all with mega-bucks, quiet as a gopher corporate sponsorship fighting a Cold War against Sun and the JVM that doesn’t exist anymore marketed by appealing to the worst instincts of otherwise unimpeachable luminaries of computing.

    There is great software written in golang by engineers I aspire to as role models (TailScale and Brad respectively as maybe the best example). I had to get serious about learning golang and how to work around its ideologically-motivated own-goals because I got serious about WebRTC and Pion (another great piece of software). But it sucks. I dread working on that part of the stack.

    Go enums do suck, but that’s because we pay a very heavy price for golang being mainstream at all: we’ve thrown away ZooKeeper and engineer-millennia of garbage-collector work and countless other treasures, it sucks oxygen out of the room on more plausible C successors like D and Jai and Nim and Zig and V and (it pains me to admit but it’s true) Rust.

    Yes there is great software in golang, tons of it. Yes there are iconic legends who are passionate about it, yes it brought new stuff to the party and the mainstream.

    But the cost was too high.

    • Mawr 29 days ago
      > and I’m being generous with “emerging” here, the Either monad is more than an emerging consensus around the right default for error handling

      It is clearly the right way, but Go's error handling already has 80% of its benefits. Switching over to a Result type at this point would require a lot of churn for little benefit.

      Sum types on the other hand...

      > it sucks oxygen out of the room on more plausible C successors like D and Jai and Nim and Zig and V and (it pains me to admit but it’s true) Rust.

      D is dead, never even heard of Jai, Nim hasn't caught on, can't believe you're mentioning V. Rust competes mostly with C++ and is unlikely to appeal to C enthusiasts. Zig is promising, but it's in very early stages.

      Go is used because it fills the glaring hole of a reasonable, modern, fast and GCd language. It reflects poorly on PL designers that we don't have a properly designed modern language in the space and have to make do with Go. Some combination of Go+Rust would be perfect, but it just does not exist.

      • pjmlp 29 days ago
        I am doing pretty well with ML derived languages, or its influence in JVM/CLR offerings + GraalVM/OpenJ9, thankfully I only get to deal with Go on DevOps related tooling, and some of it has already been rewritten into Rust.
      • randomdata 29 days ago
        > It is clearly the right way

        It's clearly not. Either<T, Err> creates a dependence between T and Err that should not exist. They are logically independent variables. The state of Err has no reason to imply anything about the state of T.

        I'd be inclined to agree that, for all practical purposes, it is the best we've got, but that doesn't mean it is right. An ideal language can do better.

  • binary132 29 days ago
    Yes. And?
  • kaba0 29 days ago
    [flagged]
    • leosanchez 29 days ago
      Personally I dislike go because of its type system.

      But I can see why people like it.

      • bayindirh 29 days ago
        It's a simple, minimal language which does a lot of things right. I see it as a better C when you don't need to touch hardware or OS kernel.

        It's great for writing small utils, or doing multithreaded things in general.

        I don't understand qualms against programming languages. It's a tool. Best one is chosen for the task at the hand and applied to the problem. That's all.

        • kelnos 29 days ago
          Sure, and some tools are better and worse than others. That's of course a subjective matter of opinion.

          I personally like things with stronger type systems than golang (and C) has to offer. If I'm going to write a small util and don't care so much about types (or performance), I'll reach for python. If I do, I'll reach for rust. Golang just never seems like the right tool for any of my jobs.

          • bayindirh 29 days ago
            Understandable. For what I do, Python is slow, and deployment is a problem (mandatory virtualenvs, etc.). I used to love Python, but Go replaced it immediately, I may say.

            If I need speed, I use C++.

            If I need something more complicated than a bash script, I use Python.

        • neonsunset 29 days ago
          This comment is an easy tell. It's so prevalent. Because no one looked at how Go works, what Go does and what its overhead is. Not even mentioning, rightfully pointed out, a complete joke of a type system that completely disregards any improvements made since 80s.
          • bayindirh 29 days ago
            > Because no one looked at how Go works, what Go does and what its overhead is.

            I develop using Go, thanks.

            > Not even mentioning, rightfully pointed out, a complete joke of a type system that completely disregards any improvements made since 80s.

            One of the designers of Go is Rob Pike, who's worked on C, UNIX and Plan 9. I think the language embodies the core principles put out by these "things" pretty well.

            If we're talking about "development time overhead" of Go, initial momentum building is a bit slow, but after it starts rolling it's deviously fast. If we're talking about "runtime overhead", I can say it's "fast enough" for what it's designed for. If we're talking about "storage overhead", you can always dynamically link it.

            • neonsunset 29 days ago
              Using the language is not the same as understanding its design and being familiar with its implementation details.
              • bayindirh 29 days ago
                True, yet I tend to read the specs, (bundled) libraries and compiler design docs of the languages I use, because I don't like to fly blind, if you pardon the term.

                I didn't advance into stdlib and compiler yet (I'm a beginner in Go, I may say), but its behavior didn't disappoint me yet, and the specs make sense.

                I have a couple of multithreaded tools on the roadmap, which will help me understand the gopher better.

                • neonsunset 29 days ago
                  It's a bad language with obtuse and verbose design, a caricature of Elixir, a mockery of Modula-2 and a cautionary tale of C.
                  • bayindirh 29 days ago
                    Which I enjoy.

                    These things are subjective, and I agree to disagree.

                    Have a nice day :)

              • AnimalMuppet 29 days ago
                True. But writing short critical posts on HN isn't the same as understanding the language, either. Why should we believe your opinion on the language to be more correct than bayindirh's?

                Frankly, you sound like a zealot, and I distrust zealots in the area of computer language design. (I distrust them in a lot of other areas, too.) I trust pragmatists a lot more.

                You've shown off a decent vocabulary, but there's far more rant than substance. Got any substance to show us why we should listen to your opinion?

        • kaba0 29 days ago
          What does it do right? It has a bad type system, has bad scoping rules (defer does it at the end of the function, really?!), bad conventions (capitalization as visibility, really? Also, unused variable disallows compilation is just absolutely idiotic, there is no sane reason for doing that. Introduce a debug mode and a prod mode, and do so in the latter only if you really want to)
          • bayindirh 29 days ago
            It reduces cognitive load, dead code, forces handling errors and forces you to be mindful of what you initialize and use.

            Don't we use -WError to force warnings as errors in C++? How is this different?

            You don't have to use defer(), yet it allows for neat tricks for tying ends in fatal situations easily.

            Its implementation of interfaces feels a bit backwards, but it allows for neat implementations of inheritance and polymorphism.

            Python uses underscore as visibility convention, how capitalization is different from that?

            Plus, it has a very responsive and well working defragmenting GC, which is great for keeping tabs on memory use.

            I'm not a big fan of static compilation by default, but it allows compile once run anywhere (in the same arch) and is an acceptable trade-off for most cases. On top of that it has at least two competent compiler implementations.

            Go's shallowness is its power, because it forces you to write boring, yet understandable code backed by a GC which reduces possibility of errors and problems a ton.

            I can throw stones to any programming language. Java for its factories of factories, Rust for limiting for what I can do with my pointers and memory, C++ for its footguns, Lisp for its parentheses, etc.

            Nobody is forcing you to use Go, Rust or anything. Select the tools you like, and works for problems you have. If we have to have an open mind to accept Rust, and oh we're bullied into that open mindedness to accept Rust as our savior, we can be open minded about Go and look from its perspective, too. Same is true for every programming language actually. From D, to Hare, to Rust, to Go, to C++, to Brainfuck and Malbolge.

            I can say there's no sane reason for doing that in myriad of programming languages, but these are implemented and in use, so there are sane reasons these things for at least some people.

            Be flexible. There's no need to be that rigid.

            • kaba0 29 days ago
              Fair, I do think that languages are tools and we should select the ones that are appropriate for a task. But that doesn’t make every tool useful, especially that languages do depend on network effects quite a lot, unlike a hammer. There was no need to introduce a language just like many others, and Go - in my opinion - fails to carve out a novel niche of the PL scene, that would have some legitimacy of existing. Rust with all its flaws is novel, there is no other memory safe low-level language (disregarding exotic/research languages). That’s a hugely important niche, so its “hype” is warranted (even though I’m not a rust fan). For Go, even if we limit ourselves to AOT compiled managed languages, there is C#, Java (with Graal), D, Haskell, OCaml, etc.

              As for your mentioned advantages:

              > reduces cognitive load

              In what way? Please don’t come with the usual marketing that “every line is easy to understand blablah”, because otherwise we would be programming in assembly. Too little abstraction is just as bad as too much.

              > dead code

              Most AOT languages do that

              > forces error handling

              It’s literally C’s errno forced on you by the compiler. I hope we can agree that C’s solution is very far from what one would mean by good error handling. It hides the actual business logic, while not even forcing you to handle it (a random if block is not error handling in itself), and accustoms people to just skip over these.

              > mindful what you initialize and use

              That’s only a problem in C and C++. Also, I don’t even agree it does a good job at it, it basically has multiple nulls for example for no sane reason.

              • bayindirh 29 days ago
                > Go - in my opinion - fails to carve out a novel niche of the PL scene, that would have some legitimacy of existing.

                People love to bash C++ and try to replace it day and night. Go is not trying to be "the next step" in programming. Its promise is simple. C, reimagined, for relatively simple tasks. Plus it adds some batteries: green threads, a GC, modern utilities like JSON, HTTP, flag support, better portability out of the box, integrated testing, etc.

                In short, it's a better C, written in the spirit of UNIX/Plan9. I didn't expect a cutting edge, exciting, "knocks my pants off" language when I started using Go. It's a mundane tool to be honest, and I prefer boring technology for myriad of reasons.

                It's designed to be boring, to get the job done. Not to excite people with cutting edge stuff, and this is the novelty of Go. A collection of mundane ideas combined with care, resulting in a superior experience with no surprises (for the most part).

                > In what way? Please don’t come with the usual marketing that “every line is easy to understand...

                No, not in a line by line basis, but in project scope. See, Go is a simplistic language. You can't do "interesting" things with it much (e.g.: I love auto-tokenization tricks in C++ iostreams, but it needs an innate understanding how "<<" works in C++ universe). This means there's no hidden "magick" under the code to unpack. As a result, the written code is shallow, transparent and boring. When you open a Go project you don't know, reading it is easy. Because there are no tricks. As a consequence, when you don't think of tricks, but of simple utilities and function calls, the state you keep in your mind becomes smaller.

                This results in two things. 1) You can see the whole picture with less energy, resulting in better architectural awareness, 2) You can resume a project much faster, because there's less context to load from memory and code structure. I resume my paused Go projects under 10 minutes. Which is fast.

                > Most AOT languages do that

                Compilers eliminate dead code, correct, but it lives in your codebase nonetheless. "No unused variables and no unused imports" make you remove the dead code from the codebase too, resulting in a leaner codebase with less head scratches. Less complex codebases means less bugs, more understanding, and faster work. See my previous point.

                > It’s literally C’s errno forced on you by the compiler. I hope we can agree that C’s solution is very far from what one would mean by good error handling.

                Personally I have no qualms with errno based error handling (and no qualms with exception handling (try/catch and friends)), plus Go's errors provide more context, because error is an object carrying much more information w.r.t. an integer. Also, it makes error handling a must, and skipping over error handling voluntary. If you use "_" instead of error and do not handle it, it's on you now. You had the chance and you skipped it. I think it's a pretty neat mechanism: "You knowingly opt out handling a possible error, and it's your responsibility now. Roll with it". Everybody says that "safety must be on by default". It's Go's safety, and it's on by default. Accepting risks is programmer's own choice.

                > That’s only a problem in C and C++.

                No. I can leave dead assignments in every language I know, and while it might be eliminated by the compiler itself, it's again cognitive load on the codebase maintainer. See my previous points.

                > Also, I don’t even agree it does a good job at it, it basically has multiple nulls for example for no sane reason.

                Every language has its peculiarities for "no sane reason" for some people. OTOH, some other people think the reasons for these peculiarities are sane and well justified.

  • dewey 30 days ago
    [flagged]
    • axitanull 29 days ago
      It just dawns to me that "complaining about the style or minor usability of the website" is a type of bike shedding that happens regularly in hacker news.

      Instead of contributing to the discussion related to the post, it's easier to just go "the website sucks."

      • ayhanfuat 29 days ago
        I don't think it is a minor annoyance. It is indeed very hard to read.
        • axitanull 29 days ago
          So to continue our bikeshedding:

          Hacker News has no shortage of resourceful people that can silently handle the issue by themselves (the first comment even said themselves that they could simply use Reader Mode!). But the main point of my reply is: look at the number of replies that focus on the style of the website, and compare that to the number of replies that focus on the point of the post/article.

          • hnuser123456 29 days ago
            This forum is primarily used by computer programmers... and I only saw one other comment chain critiquing the style. And I agree with them that the font size is far larger than you'd normally see for a body of text to the point that it's jarring. I don't have much experience with Go, so I can't comment much on the content, I'm just here to learn when it comes to the content. As far as I can tell they are vastly overcomplicating the concept of assigning variables.
          • randomdata 29 days ago
            There is no point to the article, though. Enums suck in every language that have them. There is good reason why many modern languages don’t have them at all, offering sum types instead. It doesn’t add anything to the conversation.
        • andai 29 days ago
          Font is small on my phone, but I can read it in landscape mode. Hey, I'll take it over the average news / blog site these days.
      • shp0ngle 29 days ago
        It's even forbidden by HN rules... somewhere.
      • twixfel 29 days ago
        And "stopped reading at X" (X usually being on the first line or last line, for added stupidity in either direction, and often over something completely marginal). I wonder if there is a list of template hacker news comments anywhere.
      • kelnos 29 days ago
        Yes, it's incredibly annoying (and I'm contributing to the problem by participating in this meta discussion). My general policy is any time I see a post that does this (without saying anything else substantive), I downvote and flag. I usually reserve flagging for truly egregious examples of garbage, but I make an exception for this type of post.

        And meanwhile instead of reading later comments and participating, I'm commenting on this dumb thread. Sigh.

        Edit: wow, at the time of writing, there are only three comments on this post that's actually about the article itself, with the rest complaining about the page design. C'mon HN, do better.

    • zarldev 29 days ago
      This should be better now.
    • bowsamic 29 days ago
      [flagged]