jonathan920 2 days ago

Oh no , Rust is too tough, go is no good, am i going back to java?

  • maxloh 2 days ago

    Maybe the new in-development Carbon language? It sounds promising, but it is nowhere near its 1.0 release.

    • masklinn 2 days ago

      Carbon exists only for interoperating with and transitioning off of C++. Creating a new code base in carbon doesn’t really make sense, and the project’s readme literally tells you not to do that.

      • maxloh 2 days ago

        > ... and the project’s readme literally tells you not to do that.

        Could you quote which paragraph you're talking about?

        AFAIK, interoperability with C++ code is just one of their explicit goals; they only place that as the last item in the "Language Goals" section.

        • masklinn a day ago

          > Existing modern languages already provide an excellent developer experience: Go, Swift, Kotlin, Rust, and many more. Developers that can use one of these existing languages should.

bfrog 2 days ago

Go nearly gave me carpal tunnel with the vast quantities and almost the same but not quite the same repetitive code patterns it brings along with it. I’d never use it again.

  • hu3 a day ago

    You still type most of your code?

    AI solved my issues with carpal tunnel.

    And when I'm feeling fancy, I don't even type, just command AI by voice. "handle error case".

    • bfrog a day ago

      I've yet to see AI produce anything that wasn't hot garbage. I'm perfectly fine using Rust or C without carpal tunnel issues depending on the context.

      • hu3 a day ago

        I could reply with skill issue. But a more constructive comment would be to use AI to auto-complete, in the same line, what you would have to type anyway. Not as a way to produce large swaths of code.

        • bfrog 11 hours ago

          Let me know when the legal quagmire around AI generated junk is sorted and it can run reasonably on a local desktop not on a cloud. I might give it a go again. So far it’s been trash that requires cloud access with questionable legality around who owns what.

t43562 a day ago

Cross compiling go is easy. Static binaries work everywhere. The cryptographic library is the foundation of various CAs like letsencrypt and is excellent.

The green threads are very interesting since you can create 1000s of them at a low cost and that makes different designs possible.

I think this complaining about defer is a bit trivial. The actual major problem for me is the way imports work. The fact that it knows about github and the way that it's difficult to replace a dependency there with some other one including a local one. The forced layout of files, cmd directories etc etc.

I can live with it all but modules are the things which I have wasted the most time and struggled the most.

nirui a day ago

The chosen example:

    bar, err := foo()
    if err != nil {
        return err
    }
    if err = foo2(); err != nil {
        return err
    }
Sounded more like a nitpicking.

If you really care about scope while being able to use `bar` later down, the code should be written as:

    bar, err := foo()
    if err != nil {
        return err
    }
    err = foo2() // Just reuse `err` plainly
    if err != nil {
        return err
    }
which actually overwrites `err`, opposite to "shadowing" it.

The confusing here, is that the difference between `if err != nil` and `if err = call(); err != nil` is not just style, the later one also introduces a scope that captures whatever variables got created before `;`.

If you really REALLY want to use the same `if` style, try:

    if bar, err := foo(); err != nil {
        return err
    } else if bar2, err := foo2(); err != nil {
        return err
    } else {
        [Use `bar` and `bar2` here]
        return ...
    }
sunnyps a day ago

Would the interface nil example be clearer if checking for `nil` didn't use the `==` operator? For example, with a hypothetical `is` operator:

    package main
    import "fmt"
    type I interface{}
    type S struct{}
    func main() {
        var i I
        var s *S
        fmt.Println(s, i) // nil nil
        fmt.Println(s is nil, i is nil, s == i) // t,t,f: Not confusing anymore?
        i = s
        fmt.Println(s, i) // nil nil
        fmt.Println(s is nil, i is nil, s == i) // t,f,t: Still not confusing?
    }
Of course, this means you have to precisely define the semantics of `is` and `==`:

- `is` for interfaces checks both value and interface type.

- `==` for interfaces uses only the value and not the interface type.

- For structs/value, `is` and `==` are obvious since there's only a value to check.

tapirl a day ago

Go indeed has its problems. But the ones described in this article just prove the author is a Go newbie.

  • thomashabets2 a day ago

    I won't be dumping credentials, but perhaps the newbie is you?

    I also review a lot of Go code. I see these problems all the time from other people.

lvl155 2 days ago

I think a lot of people got on the Go train because of Google and not necessarily because it was good. There was a big adoption in Chinese tech scene for example. I personally think Rust/Go/Zig and other modern languages suffer a bit from trying too hard not to be C/C++/Java.

  • wewxjfq 2 days ago

    Go was a breath of fresh air and pretty usable right from the start. It felt like a neat little language with - finally - a modern standard library. Fifteen years ago, that was a welcome change. I think it's no surprise that Go and Node.js both got started and took off around the same time. People were looking something modern, lightweight, and simple and both projects delivered that.

sublimefire 2 days ago

This post is just an attention grabbing rage bate. Listed issues are superficial unless the person is a bit far into the spectrum. There is no good datapoint which would weigh the issues against real world problems, i.e. how much does it cost. Even the point about ram is weak without the data.

baby a day ago

Sum types is the one big thing missing IMO, the language got a LOT of things right otherwise

kragen a day ago

There are a couple of minor errors in this post, but mostly it consists of someone getting extremely overexcited about minor problems in Golang they have correctly identified.

  • thomashabets2 a day ago

    Author here. I may not be able to deny the second part, but I would love to hear anything you think is factually incorrect or that I may have been unclear about. Always happy to be corrected.

    (something that's not a minor error, that someone else pointed out, is that Python isn't strictly refcounted. Yeah, that's why emphasized "almost" and "pretty much". I can't do anything about that kind of critique)

    • kragen a day ago

      Oh, I meant that you were mistaken about handling nom-UTF-8 filenames (see https://news.ycombinator.com/item?id=44986040) and in 90% of cases a deferred mutex unlock makes things worse instead of better.

      The kind of general reason you need a mutex is that you are mutating some data structure from one valid state to another, but in between, it's in an inconsistent state. If other code sees that inconsistent state, it might crash or otherwise misbehave. So you acquire the mutex beforehand and release it once it's in the new valid state.

      But what happens if you panic during the modification? The data structure might still be in an inconsistent state! But now it's unlocked! So other threads that use the inconsistent data will misbehave, and now you have a very tricky bug to fix.

      This doesn't always apply. Maybe the mutex is guarding a more systemic consistency condition, like "the number in this variable is the number of messages we have received", and nothing will ever crash or otherwise malfunction if some counts are lost. Maybe it's really just providing a memory fence guarding against a torn read. Maybe the mutex is just guarding a compare-and-swap operation written out as a compare followed by a swap. But in cases like these I question whether you can really panic with the mutex held!

      This is why Java deprecated Thread.stop. (But Java does implicitly unlock mutexes when unwinding during exception handling, and that does cause bugs.)

      This is only vaguely relevant to your topic of whether Golang is good or not. Explicit error handling arguably improves your chances of noticing the possibility of an error arising with the mutex held, and therefore handling it correctly, but you correctly pointed out that because Go does have exceptions, you still have to worry about it. And cases like these tend to be fiendishly hard to test—maybe it's literally impossible for a test to make the code you're calling panic.

novoreorx 15 hours ago

I would love to see a Go fork that addresses some of the problems mentioned in this article. I will contribute a name for this project: "mygo"

commandersaki 2 days ago

“What color is your nil?” — The two billion dollar mistake.

Talk about hyperbole.

tonyhart7 a day ago

Yeah the language doesn't feel next gen

I can see why people pick it but its major step up in convenience rather than major step up in evolution programming language itself

  • Mawr a day ago

    > its major step up in convenience rather than major step up in evolution programming language itself

    The distinction you're making here does not exist IMO. Convenience is the entire point of design.

  • cyberpunk a day ago

    I've written a fair chunk of go in $dayjob and I have t say it's just... Boring. I know that sounds like a weird thing to complain about, but I just can't get enthused for anything I write in go. It's just.. Meh. Not sure why that is, guess it doesn't really click for me like other languages have in the past.

    It's a good language for teams, for sure, though.

    • nasretdinov a day ago

      No, it's absolutely meant to be boring by design. It's also a downside, obviously, but it's easily compensated by working on something that's already challenging. The language standing out of your way is quite useful in such cases

    • prisenco a day ago

      Go being boring is exactly why I use it.

Mawr a day ago

Congratulations, you have found a few pain points in a language. Now as a scientific exercise apply the same reasoning to a few others. Will the number of issues you find multiplied by their importance be greater or lower than the score for Go? There you go, that's the entire problem - Go is bad, but there is no viable alternative in general.

ironmagma a day ago

Another annoying thing Go proponents say is that it is simple. It is not. And even if it was, the code you write with a simple language is not automatically simple. Take the k8s control plane for example; some of the most convoluted and bulky code that exists, and it’s all in Go.

Night_Thastus a day ago

Fascinating. Coming from C++ I can't imagine not having RAII. That seems so wordy and painful. And that nil comparison is...gross.

I don't get how you can assign an interface to be a pointer to a structure. How does that work? That seems like a compile error. I don't know much about Go interfaces.

reactordev a day ago

There were points in this article that made me feel like Rob Schneider in Demolition Man saying "He doesn't know about the three sea shells!" but there were a couple points made that were valid.

the nil issue. An interface, when assigned a struct, is no longer nil even if that struct is nil - probably a mistake. Valid point.

append in a func. Definitely one of the biggest issues is that slices are by ref. They did this to save memory and speed but the append issue becomes a monster unless abstracted. Valid point.

err in scope for the whole func. You defined it, of course it is. Better to reuse a generic var than constantly instantiate another. The lack of try catch forces you to think. Not a valid point.

defer. What is the difference between a scope block and a function block? I'll wait.

  • moogly a day ago

    Would have been more interested in a rebuttal of the claims you consider invalid instead of an overly verbose +1 that added nothing.

leecommamichael a day ago

The Odin Programming Language has meaningful responses to all concerns. The design of the language is impeccable.

0x000xca0xfe 2 days ago

> If you stuff random binary data into a string, Go just steams along, as described in this post.

> Over the decades I have lost data to tools skipping non-UTF-8 filenames. I should not be blamed for having files that were named before UTF-8 existed.

Umm.. why blame Go for that?

  • thomashabets2 a day ago

    Author here.

    What I intended to say with this is that ignoring the problem if invalid UTF-8 (could be valid iso8859-1) with no error handling, or other way around, has lost me data in the past.

    Compare this to Rust, where a path name is of a different type than a mere string. And if you need to treat it like a string and you don't care if it's "a bit wrong" (because it's for being shown to the user), then you can call `.to_string_lossy()`. But it's be more hard to accidentally not handle that case when exact name match does matter.

    When exactness matters, `.to_str()` returns `Option<&str>`, so the caller is forced to deal with the situation that the file name may not be UTF-8.

    Being sloppy with file name encodings is how data is lost. Go is sloppy with strings of all kinds, file names included.

    • 0x000xca0xfe a day ago

      Thanks for your reply. I understand that encoding the character set in the type system is more explicit and can help find bugs.

      But forcing all strings to be UTF-8 does not magically help with the issue you described. In practice I've often seen the opposite: Now you have to write two code paths, one for UTF-8 and one for everything else. And the second one is ignored in practice because it is annoying to write. For example, I built the web server project in your other submission (very cool!) and gave it a tar file that has a non-UTF-8 name. There is no special handling happening, I simply get "error: invalid UTF-8 was detected in one or more arguments" and the application exits. It just refuses to work with non-UTF-8 files at all -- is this less sloppy?

      Forcing UTF-8 does not "fix" compatibility in strange edge cases, it just breaks them all. The best approach is to treat data as opaque bytes unless there is a good reason not to. Which is what Go does, so I think it is unfair to blame Go for this particular reason instead of the backup applications.

      • thinkharderdev a day ago

        > It just refuses to work with non-UTF-8 files at all -- is this less sloppy?

        You can debate whether it is sloppy but I think an error is much better than silently corrupting data.

        > The best approach is to treat data as opaque bytes unless there is a good reason not to

        This doesn't seem like a good approach when dealing with strings which are not just blobs of bytes. They have an encoding and generally you want ways to, for instance, convert a string to upper/lowercase.

      • thomashabets2 a day ago

        Can't say I know the best way here. But Rust does this better than anything I've seen.

        I don't think you need two code paths. Maybe your program can live its entire life never converting away from the original form. Say you read from disk, pick out just the filename, and give to an archive library.

        There's no need to ever convert that to a "string". Yes, it could have been a byte array, but taking out the file name (or maybe final dir plus file name) are string operations, just not necessarily on UTF-8 strings.

        And like I said, for all use cases where it just needs to be shown to users, the "lossy" version is fine.

        > I simply get "error: invalid UTF-8 was detected in one or more arguments" and the application exits. It just refuses to work with non-UTF-8 files at all -- is this less sloppy?

        Haha, touche. But yes, it's less sloppy. Would you prefer that the files were silently skipped? You've created your archive, you started the webserver, but you just can't get it to deliver the page you want.

        In order for tarweb to support non-UTF-8 in filenames, the programmer has to actually think about what that means. I don't think it means doing a lossy conversion, because that's not what the file name was, and it's not merely for human display. And it should probably not be the bytes either, because tools will likely want to send UTF-8 encoded.

        Or they don't. In either case unless that's designed, implemented, and tested, non-UTF-8 in filenames should probably be seen as malformed input. For something that uses a tarfile for the duration of the process's life, that probably means rejecting it, and asking the user to roll back to a previous working version or something.

        > Forcing UTF-8 does not "fix" compatibility in strange edge cases

        Yup. Still better than silently corrupting.

        Compare this to how for Rust kernel work they apparently had to implement a new Vec equivalent, because dealing with allocation failures is a different thing in user and kernel space[1], and Vec push can't fail.

        Similarly, Go string operations cannot fail. And memory allocation issues has reasons that string operations don't.

        [1] a big separate topic. Nobody (almost) runs with overcommit off.

pif 2 days ago

It doesn't need to be good because it is not meant for good developers.

  • Buttons840 2 days ago

    And it's perfect for most business software, because most businesses are not focused on building good software.

    Go has a good-enough standard library, and Go can support a "pile-of-if-statements" architecture. This is all you need.

    Most enterprise environments are not handled with enough care to move beyond "pile-of-if-statements". Sure, maybe when the code was new it had a decent architecture, but soon the original developers left and then the next wave came in and they had different ideas and dreamed of a "rewrite", which they sneakily started but never finished, then they left, and the 3rd wave of developers came in and by that point the code was a mess and so now they just throw if-statements onto the pile until the Jira tickets are closed, and the company chugs along with its shitty software, and if the company ever leaks the personal data of 100 million people, they aren't financially liable.

    • theshrike79 2 days ago

      Go has extremely robust linters just for the corporate use-case. And gofmt.

      Every piece of code looks the same and can be automatically, neutrally, analysed for issues.

crinkly 2 days ago

I dislike Go but I haven’t found anything else I dislike less.

enigma101 19 hours ago

Then don't use it yiks. Everyone got an opinion these days.

Shawnecy a day ago

For too many aspects for my liking, Go moves complexity out of the language and into your code where you get to unavoidably deal with the cognitive load. It's fine if you can keep things small and simple, but beyond a certain complexity, it's a hard pass for me.

golly_ned a day ago

Of all the languages one could accuse of being hermetically designed in an ivory tower, Go would be the second-least likely.

4ndrewl a day ago

Ok, it's not a good fit for you.

Don't use it I guess and ignore all the X is not good posts for language X you do decide to use?

gwd 2 days ago

Anyone want to try to explain what he's on about with the first example?

    bar, err := foo()
    if err != nil {
      return err
    }
    if err := foo2(); err != nil {
      return err
    }
The above (which declares a new value of err scoped to the second if statement) should compile right? What is it that he's complaining about?

EDIT: OK, I think I understand; there's no easy way to have `bar` be function-scoped and `err` be if-scoped.

I mean, I'm with him on the interfaces. But the "append" thing just seems like ranting to me. In his example, `a` is a local variable; why would assigning a local variable be expected to change the value in the caller? Would you expect the following to work?

    int func(a *MyStruct) {
      a = &MyStruct{...}
    }
If not why would you expect `a = apppend(a, ...)` to work?
  • thomashabets2 2 days ago

    > why would assigning a local variable be expected to change the value in the caller?

    I think you may need to re-read. My point is that it DOES change the value in the caller. (well, sometimes) That's the problem.

    • gwd a day ago

      Oh, I see. I mean, yeah, the relationships between slices and arrays is somewhat subtle; but it buys you some power as well. I came to golang after decades of C, so I didn't have much trouble with the concept.

      I'm afraid I can only consider that a taste thing.

      EDIT: One thing I don't consider a taste thing is the lack of the equivalent of a "const *". The problem with the slice thing is that you can sort of sometimes change things but not really. It would be nice if you could be forced to pass either a pointer to a slice (such that you can actually allocate a new backing array and point to it), or a non-modifiable slice (such that you know the function isn't going to change the slice behind your back).

  • mrweasel 2 days ago

    That might be it, but I wondered about that one, as well as the append complaint. It seems like the author disagree with scoping rules, but they aren't really any different than a lot of other languages.

    If someone really doesn't like the reuse of err, there's no reason why they couldn't create separate variable, e.g. err_foo and err_foo2. There's not no reason to not reuse err.

  • sublimefire 2 days ago

    edit: the main rant about err was that it is left in scope but I believe the author does not like that

  • terminalbraid 2 days ago

    You didn't copy the code correctly from the first example.

    • gwd 2 days ago

      Well no, the second "if" statement is a red herring. Both of the following work:

          bar, err := foo()
          if err != nil {
            return err
          }
          if err = foo2(); err != nil {
            return err
          }
      
      and

          bar, err := foo()
          if err != nil {
            return err
          }
          if err := foo2(); err != nil {
            return err
          }
      
      He even says as much:

      > Even if we change that to :=, we’re left to wonder why err is in scope for (potentially) the rest of the function. Why? Is it read later?

      My initial reaction was: "The first `err` is function-scope because the programmer made it function-scope; he clearly knows you can make them local to the if, so what's he on about?`

      It was only when I tried to rewrite the code to make the first `err` if-scope that I realized the problem I guess he has: OK, how do you make both `err` variable if-scope while making `bar` function-scope? You'd have to do something like this:

          var bar MyType
          if lbar, err := foo(); err != nil {
            return err
          } else {
            bar = lbar
          }
      
      Which is a lot of cruft to add just to restrict the scope of `err`.
arewethereyeta a day ago

there are plenty of other languages. I dont get this love-hate type of speech like golang itself owes you an apology.

SkiFire13 a day ago

> Two types of nil

What in the javascript is this.

  • mdaniel a day ago

    I get bitten by the "nil interface" problem if I'm not paying a lot of attention since golang makes a distinction between the "enclosing type" and the "receiver type"

      package main
    
      import "fmt"
    
      type Foo struct{
       Name string
      }
    
      func (f *Foo) Kaboom() {
       fmt.Printf("hello from Kaboom, f=%s\n", f.Name)
      }
    
      func NewKaboom() interface{ Kaboom() } {
       var p *Foo = nil
       return p
      }
    
      func main() {
       obj := NewKaboom()
    
       fmt.Printf("obj == nil? %v\n", obj == nil)
       // The next line will panic (because method receives nil *Foo)
       obj.Kaboom()
      }
    
    
    
      go run fred.go
      obj == nil? false
      panic: runtime error: invalid memory address or nil pointer dereference
gnfargbl 2 days ago

As a long-time Go programmer I didn't understand the comment about two types of nil because I have never experienced that issue, so I dug into it.

It turns out to be nothing but a misunderstanding of what the fmt.Println() statement is actually doing. If we use a more advanced print statement then everything becomes extremely clear:

    package main

    import (
      "fmt"
      "github.com/k0kubun/pp/v3"
    )

    type I interface{}
    type S struct{}

    func main() {
      var i I
      var s *S

      pp.Println(s, i)                        // (*main.S)(nil) nil
      fmt.Println(s == nil, i == nil, s == i) // true true false

      i = s

      pp.Println(s, i)                        // (*main.S)(nil) (*main.S)(nil)
      fmt.Println(s == nil, i == nil, s == i) // true false true
    }
The author of this post has noted a convenience feature, namely that fmt.Println() tells you the state of the thing in the interface and not the state of the interface, mistaken it as a fundamental design issue and written a screed about a language issue that literally doesn't exist.

Being charitable, I guess the author could actually be complaining that putting a nil pointer inside a nil interface is confusing. It is indeed confusing, but it doesn't mean there are "two types" of nil. Nil just means empty.

  • tux3 2 days ago

    The author is showing the result of s==nil and i==nil, which are checks that you would have to do almost everywhere (the so called "billion dollar mistake")

    It's not about Printf. It's about how these two different kind of nil values sometimes compare equal to nil, sometimes compare equal to each other, and sometimes not

    Yes there is a real internal difference between the two that you can print. But that is the point the author is making.

    • gnfargbl a day ago

      It's a contrived example which I have never really experienced in my own code (and at this point, I've written a lot of it) or any of my team's code.

      Go had some poor design features, many of which have now been fixed, some of which can't be fixed. It's fine to warn people about those. But inventing intentionally confusing examples and then complaining about them is pretty close to strawmanning.

      • the_mitsuhiko a day ago

        > It's a contrived example which I have never really experienced in my own code (and at this point, I've written a lot of it) or any of my team's code.

        It's confusing enough that it has an FAQ entry and that people tried to get it changed for Go 2. Evidently people are running in to this. (I for sure did)

      • k_roy a day ago

        That's really my problem with these kind of critiques.

        EVERY language has certain pitfalls like this. Back when I wrote PHP for 20+ years I had a Google doc full of every stupid PHP pitfall I came across.

        And they were always almost a combination of something silly in the language, and horrible design by the developer, or trying to take a shortcut and losing the plot.

      • tux3 a day ago

        I believe you that you've never hit it, it's definitely not an everyday problem. But they didn't make it up, it does bite people from time to time.

        It's sort of a known sharp edge that people occasionally cut themselves on. No language is perfect, but when people run into them they rightfully complain about it

  • thomashabets2 2 days ago

    Author here. No, I didn't misunderstand it. Interface variables have two types of nil. Untyped, which does compare to nil, and typed, which does not.

    What are you trying to clarify by printing the types? I know what the types are, and that's why I could provide the succinct weird example. I know what the result of the comparisons are, and why.

    And the "why" is "because there are two types of nil, because it's a bad language choice".

    I've seen this in real code. Someone compares a variable to nil, it's not, and then they call a method (receiver), and it crashes with nil dereference.

    Edit, according to this comment this two-types-of-null bites other people in production: https://news.ycombinator.com/item?id=44983576

    • gnfargbl a day ago

      > Author here. No, I didn't misunderstand it. Interface variables have two types of nil. Untyped, which does compare to nil, and typed, which does not.

      There aren't two types of nil. Would you call an empty bucket and an empty cup "two types of empty"?

      There is one nil, which means different things in different contexts. You're muddying the waters and making something which is actually quite straightforward (an interface can contain other things, including things that are themselves empty) seem complicated.

      > I've seen this in real code. Someone compares a variable to nil, it's not, and then they call a method (receiver), and it crashes with nil dereference.

      Sure, I've seen pointer-to-pointer dereferences fail for the same reason in C. It's not particularly different.

[removed] 11 hours ago
[deleted]
skywhopper a day ago

God, this sort of article is so boring. Go is a great language, as evidenced by the tremendous amount of excellent software that’s been written in it. Are there some rough points? Sure.

But that’s all this is, is a list of annoyances the author experiences when using Go. Great, write an article about the problems with Go, but don’t say “therefore it’s a bad language”.

While I agree with many of the points brought up, none of them seems like such a huge issue that it’s even worth discussing, honestly. So you have different taste the the original designers. Who cares? What language do you say is better? I can find just as many problems with that language.

Also, defer is great.

zwnow 2 days ago

> Wait, what? Why is err reused for foo2()? Is there’s something subtle I’m not seeing? Even if we change that to :=, we’re left to wonder why err is in scope for (potentially) the rest of the function. Why? Is it read later?

First time its assigned nil, second time its overwritten in case there's an error in the 2nd function. I dont see the authors issue? Its very explicit.

  • thomashabets2 2 days ago

    Author here: I'm not talking about the value. I'm talking about the lifetime of the variable.

    After checking for nil, there's no reason `err` should still be in scope. That's why it's recommended to write `if err := foo(); err != nil`, because after that, one cannot even accidentally refer to `err`.

    I'm giving examples where Go syntactically does not allow you to limit the lifetime of the variable. The variable, not its value.

    You are describing what happens. I have no problem with what happens, but with the language.

    • zwnow 2 days ago

      Why does the lifetime even matter?

      • thomashabets2 a day ago

        I gave an example in the post, but to spell it out: Because a typo variable is not caught, e.g. as an unused variable.

        The example from the blog post would fail, because `return err` referred to an `err` that was no longer in scope. It would syntactically prevent accidentally writing `foo99()` instead of `err := foo99()`.

  • lowmagnet 2 days ago

    I'll have to read the rest later but this was an unforced error on the author's part. There is nothing unclear about that block of code. If err isn't but, it was set, and we're no longer in the function. If it's not, why waste an interface handle?

baq 2 days ago

> Though Python is almost entirely refcounted, so one can pretty much rely on the __del__ finalizer being called.

yeah no. you need an acyclic structure to maybe guarantee this, in CPython. other Python implementations are more normal in that you shouldn't rely on finalizers at all.

  • sgarland 2 days ago

    I love Python, but the sheer number of caveats and warnings for __del__ makes me question if this person has ever read the docs [0]. My favorite WTF:

    > It is possible (though not recommended!) for the __del__() method to postpone destruction of the instance by creating a new reference to it. This is called object resurrection.

    [0]: https://docs.python.org/3/reference/datamodel.html#object.__...

    • guappa 2 days ago

      How does this relate to the claim of the parent comment that cyclic structures are never freed in python (which is false, btw)?

      • sgarland 2 days ago

        When I replied, the only thing the comment said was “yeah no.” I was agreeing that __del__ is fraught with peril.

        Reading: cyclic GC, yes, the section I linked explicitly discusses that problem, and how it’s solved.

      • baq 2 days ago

        this is not what I claim, BTW.

  • thomashabets2 2 days ago

    Author here.

    Yes, yes. Hence the words "almost" and "pretty much". For exactly this reason.

moomoo11 a day ago

This reads like your generic why js sucks why c sucks why py sucks etc.

Use the language where it makes sense. You do know what if you have an issue that the language fails at, you can solve that particular problem in another language and.. call that code?

We used to have a node ts service. We had some computationally heavy stuff, we moved that one part to Go because it was good for that ONE thing. I think later someone ported that one thing to Rust and it became a standalone project.

Idk. It’s just code. Nobody really cares, we use these tools to solve problems.

api a day ago

Go passes on a lot of ideas that are popular in academic language theory and design, with mixed but I think mostly positive results for its typical use cases.

Its main virtues are low cognitive load and encouraging simple straightforward ways of doing things, with the latter feeding into the former.

Languages with sophisticated powerful type systems and other features are superior in a lot of ways, but in the hands of most developers they are excuses to massively over-complicate everything. Sophomore developers (not junior but not yet senior) love complexity and will use any chance to add as much of it as they can, either to show off how smart they are, to explore, or to try to implement things they think they need but actually don't. Go somewhat discourages this, though devs will still find a way of course.

Experienced developers know that complexity is evil and simplicity is actually the sign of intelligence and skill. A language with advanced features is there to make it easier and simpler to express difficult concepts, not to make it more difficult and complex to express simple concepts. Every language feature should not always be used.

  • danenania a day ago

    Oh yeah. Said another way, it discourages nerd-sniping, which in practice is a huge problem with functional programming and highly expressive type systems.

    You end up creating these elegant abstractions that are very seductive from a programmer-as-artist perspective, but usually a distraction from just getting the work done in a good enough way.

    You can tell that the creators of Go are very familiar with engineer psychology and what gets them off track. Go takes away all shiny toys.

zzzeek a day ago

Go is the kind of language you use at your job, and you necessarily need to have dozens of linters and automated code quality checks set up to catch all the gotchas, like the stuff with "err" here, and nobody is ever going to get any joy from any of it. the entire exercise of "you must return and consume an error code from all functions" has been ridiculous from go's inception, it looked ridiculous to me back when I saw it in 2009, and now that I have to use it for k8s stuff at work, it's exactly as ridiculous as it seemed back then.

With all of that, Go becomes the perfect language for the age of LLMs writing all the code. Let the LLMs deal with all the boilerplate and misery of Go, while at the same time its total lack of elegance is also well suited to LLMs which similarly have the most dim notions of code elegance.

m0llusk a day ago

None of these objections seem at all serious to me, then the piece wraps up with "Why do I care about memory use? RAM is cheap." Excuse me? Memory bloat effects performance and user experience with every operation. Careful attention to software engineering should avoid or minimize these problems and emphasize the value of being tidy with memory use.

z0r a day ago

Someone send this man a peer bonus

porridgeraisin 2 days ago

I wrote a small explainer on the typed-vs-untyped nil issue. It is one of the things that can actually bite you in production. Easy to miss it in code review.

Here's the accompanying playground: https://go.dev/play/p/Kt93xQGAiHK

If you run the code, you will see that calling read() on ControlMessage causes a panic even though there is a nil check. However, it doesn't happen for Message. See the read() implementation for Message: we need to have a nil check inside the pointer-receiver struct methods. This is the simplest solution. We have a linter for this. The ecosystem also helps, e.g protobuf generated code also has nil checks inside pointer receivers.

  • xlii 2 days ago

    After spending some time in lower level languages Go IMO makes much more sense. Your example:

    First one - you have an address to a struct, you pass it, all good.

    Second case: you set address of struct to "nil". What is nil? It's an address like anything else. Maybe it's 0x000000 or something else. At this point from memory perspective it exists, but OS will prevent you from touching anything that NULL pointer allows you to touch.

    Because you don't touch ANYTHING nothing fails. It's like a deadly poison in a box you don't open.

    Third example id the same as second one. You have a IMessage but it points to NULL (instead NULL pointing to deadly poison).

    And in fourth, you finally open the box.

    Is it magic knowledge? I don't think so, but I'm also not surprised about how you can modify data through slice passing.

    IMO the biggest Go shortcoming is selling itself as a high level language, while it touches more bare metal that people are used to touch.

[removed] 2 days ago
[deleted]
YetAnotherNick 2 days ago

What does this mean? Do they just use recover and keep bad data?

> The standard library does that. fmt.Print when calling .String(), and the standard library HTTP server does that, for exceptions in the HTTP handlers.

Apart from this most doesn't seem that big of a deal, except for `append` which is truly a bad syntax. If you doing it inplace append don't return the value.

  • thomashabets2 a day ago

    The standard library recovers from the panic, and program continues.

    This means that if you do:

        func (f Foo) String() string {
          some.Lock()
          t := get_something()
          some.Unlock()
          t = transform_it(t)
          return t
        }
    
    And `get_something()` panics, then the program continues with a locked mutex. There are more dangerous things than a deadlocked program, of course.

    It's non-optional to use defer, and thus write exception safe code. Even if you never use exceptions.

0xbadcafebee a day ago

What's popular and what's good are rarely (if ever) the same thing.

Python sucks balls but it has more fanboys than a K-pop idol. Enforced whitespace? No strong typing? A global interpreter lock? Garbage error messages? A package repository you can't search (only partially because it's full of trash packages and bad forks) with random names and no naming convention? A complete lack of standardization in installing or setting up applications/environments? 100 lines to run a command and read the stout and stderr in real time?

The real reason everyone uses Python and Go is because Google used them. Otherwise Python looks like BASIC with objects and Go is an overhyped niche language.

fullstackchris a day ago

all these lame folks complaining about "what go could have been"... is this not HACKER news? cant you go and build your own, "better" language? but you won't, you'll just complain

defraudbah a day ago

lol, first I thought - "cmon, errors are bad, stop beating the dead horse", but then the fan started, good article, had a lot of fun reading it

naikrovek 2 days ago

Show me a programming language that does not have annoying flaws and I'll show you a programming language that does not yet exist, and probably won't ever exist.

I really like Go. It scratches every itch that I have. Is it the language for your problems? I don't know, but very possibly that answer is "no".

Go is easy to learn, very simple (this is a strong feature, for me) and if you want something more, you can code that up pretty quickly.

The blog article author lost me completely when they said this:

> Why do I care about memory use? RAM is cheap.

That is something that only the inexperienced say. At scale, nothing is cheap; there is no cheap resource if you are writing software for scale or for customers. Often, single bytes count. RAM usage counts. CPU cycles count. Allocations count. People want to pretend that they don't matter because it makes their job easier, but if you want to write performant software, you better have that those cpu cache lines in mind, and if you have those in mind, you have memory usage of your types in mind.

  • Capricorn2481 a day ago

    > At scale, nothing is cheap; there is no cheap resource if you are writing software for scale or for customers. Often, single bytes count. RAM usage counts. CPU cycles count. Allocations count

    Well if maximalist performance tuning is your stated goal, to the point that single bytes count, I would imagine Go is a pretty terrible choice? There are definitely languages with a more tunable GC and more cache line friendly tools than Go.

    But honestly, your comment reads more like gatekeeping, saying someone is inexperienced because they aren't working with software at the same scale as you. You sound equally inexperienced (and uninterested) with their problem domain.

    • naikrovek 8 hours ago

      “RAM is cheap” and “CPU is cheap” are the calling cards of the inexperienced programmer who is trying to justify the poor quality of their code. End of statement.

porridgeraisin 2 days ago

> Previous posts Why Go is not my favourite language and Go programs are not portable have me critiquing Go for over a decade.

I chuckled

  • colesantiago 2 days ago

    Same here, I don't know if this makes him Go's biggest fan or this is actually genuinely sad.

    Never had any problems with Go as it makes me millions each year.

    • neuroelectron 2 days ago

      Never had a problem with Enron because I sold it when it was high.

voodooEntity 2 days ago

As someone who for >10 years writes golang and has written some bigger codebases using it, this are my takes on this articles claims:

:Error variable Scope -> Yes can be confusing at the beginning, but if you have some experience it doesnt really matter. Would it be cool to scope it down?`Sure, but it feels like here is something blown up to an "issue" where i would see other things to alot more important for the go team to revisit. Regarding the error handling in go, some hate it , some love it : i personally like it (yes i really do) so i think its more a preference than a "bad" thing.

:Two types of nil -> Funny, i never encountered this in > 10 years of go with ALOT of work in pointer juggling, so i wonder in which reality this hits your where it cant be avoided. Tho confusing i admit

:It’s not portable -> I have no opinion here since i work on unix systems only and i have my compiled binaries specific shrug dont see any issue here either.

:append with no defined ownership -> I mean... seriously? Your test case, while the results may be unexpected, is a super wierd one. Why you you append a mid field, if you think about what these functions do under the hood your attemp actualyl feels like you WANT to procude strange behaviour and things like that can be done in any language.

:defer is dumb -> Here i 100% agree - from my pov it leads to massive resource wasting and in certain situations it can also create strange errors, but im not motivated to explain this - ill just say defer, while it seems usefull, from my pov is a bad thing and should not be used.

:The standard library swallows exceptions, so all hope is lost -> "So all hope is lost" i mean you already left the realm of objectiveness long before tbut this really tops it. I wrote some quite big go applications and i never had a situation where i could not handle an exception simply by adjusting my code in a way that i prevent it from even happening. Again - i feel like someone is just in search of things to complain that could simply be avoided. (also in case someone comes up with a super specific probably once in a million case, well alrways keep in mind that language design doesnt orient on the least occuring thing).

:Sometimes things aren’t UTF-8 -> I wont bother to read another whole article, if its important include an example. I have dealth with different encodings (web crawler) and i could handle all of them.

:Memory use -> What you describe is one of the design decisions im not absolutly happy with, the memory handling. But than, one of my golang projects is an in memory graph storage/database - which in one of my cases run for ~2years without restart and had about 18GB of dataset stored in it. It has a lot of mutex handling (regarding your earlier complain with exxceptions, never had one) and it btw run as backend of a internet facing service so it wasnt just fed internal data.

--------------------

Finally i wanne say : often things come down to personal preference. I could spend days raging about javascript, java, c++ or some other languages, but whatfor? Pick the language that fits your use case and your liking, dont pick one that doesnt and complain about it.

Also , just to show im not just a big "golang is the best" fanboy, because it isnt - there are things to critizize like the previously mentioned memory handling.

While i still think you just created memory leaks in your app, golang had this idea of "arenas" which would enable the code to manage memory partly himself and therefor developt much more memory efficient applications. This has stalled lately and i REALLY hope the go team will pick it up again and make this a stable thing to use. I probably would update all of my bigger codebases using it.

Also - and thats something thats annoying me ALOT beacuse it made me spend alot of hours - the golang plugin system. I wrote an architecture to orchestrate processing and for certain reasons i wanted to implement the orchestrated "things" as plugins. But the plugin system as it is rn can only be described as the torments of hell. I messed with it for like 3 years till i recently dropped the plugin functionality and added the stuff directly. Plugins are a very powerfull thing and a good plugin system could be a great thing, but in its current state i would recommend noone to touch it.

These are just two points, i could list some more but the point i want to get to is : there are real things you can critizize instead of things that you create yourself or that are language design decision that you just dont like. Im not sure if such articles are the rage of someone who just is bored or its ragebait to make people read it. Either way its not helping anyone.

  • thomashabets2 2 days ago

    Author here.

    :Two types of nil

    Other commenters have. I have. Not everyone will. Doesn't make it good.

    :append with no defined ownership

    I've seen it. Of course one can just "not do that", but wouldn't it be nice if it were syntactically prevented?

    :It’s not portable ("just Unix")

    I also only work on Unix systems. But if you only work on amd64 Linux, then portability is not a concern. Supporting BSD and Linux is where I encounter this mess.

    :All hope is lost

    All hope is lost specifically on the idea of not needing to write exception safe code. If panics did always crash the problem, then that'd be fine. But no coding standard can save you from the standard library, so yes, all hope about being able to pretend panic exits the problem, is lost.

    You don't need to read my blog posts. Looking forward to reading your, much better, critique.

keyle 2 days ago

I use Go daily for work, alongside Dart, Python.

I say switching to Go is like a different kind of Zen. It takes time, to settle in and get in the flow of Go... Unlike the others, the LSP is fast, the developer, not so much. Once you've lost all will to live you become quite proficient at it. /s

  • theshrike79 2 days ago

    I've been writing small Go utilities for myself since the Go minor version number was <10

    I can still check out the code to any of them, open it and it'll look the same as modern code. I can also compile all of them with the latest compiler (1.25?) and it'll just work.

    No need to investigate 5 years of package manager changes and new frameworks.

  • aloukissas 2 days ago

    I also sing "Fade to Black" when I have to write go :D

    • tialaramex a day ago

      I was like "Have I ever actually heard that?" and the answer turns out to be "No" so now I have (it's a Metallica track about suicidal ideation, whether it's good idea to listen to it while writing Go I could not say and YMMV).

  • written-beyond 2 days ago

    My developer experience was similar to rust but more frustrating because of the lax typing.

    ISTG if I get downvoted for sharing my opinion I will give up on life.

asmor 2 days ago

[flagged]

  • guappa 2 days ago

    I've had library code cause a segmentation fault. I think that is even worse.

    • bhickey 2 days ago

      I've had Go's garbage collector free memory that it never allocated in the first place.

akoboldfrying 2 days ago

defer is no worse than Java's try-with-resources. Neither is true RAII, because in both cases you, the caller, need to remember to write the wordy form ("try (...) {" or "defer ...") instead of the plain form ("..."), which will still compile but silently do the wrong thing.

  • xyzzyz 2 days ago

    Sure, true RAII would be improvement over both, but the author's point is that Java is an improvement over Go, because the resource acquisition is lexical scoped, not function-scoped. Imagine if Java's `try (...) { }` didn't clear the resource when the try block ends, but rather when the wrapping method returns. That's how Go's defer works.

    • akoboldfrying 2 days ago

      Can't you create a new block scope in Go? If not, I agree. If so, just do that if you want lexical scoping?

      • Jtsummers a day ago

        defer is not block scoped in Go, it's function scoped. So if you want to defer a mutex unlock it will only be executed at the end of the function even if placed in a block. This means you can't do this (sketch):

          func (f *Foo) foo() {
              // critical section
              {
                   f.mutex.Lock()
                   defer f.mutex.Unlock()
                   // something with the shared resource
              }
              // The lock is still held here, but you probably didn't want that
          }
        
        You can call Unlock directly, but then if there's a panic it won't be unlocked like it would be in the above. That can be an issue if something higher in the call stack prevents the panic from crashing the entire program, it would leave your system in a bad state.

        This is the key problem with defer. It operates a lot like a finally block, but only on function exit which means it's not actually suited to the task.

        And as the sibling pointed out, you could use an anonymous function that's immediately called, but that's just awkward, even if it has become idiomatic.

      • gf000 2 days ago

        You have to create an anonymous function.

elktown 2 days ago

Is there anything that soothes devs more than developing a superiority complex of their particular tooling? And then the unquenchable thirst to bash "downwards"? I find it so utterly pathetic.

antiquark 2 days ago

I'm still appalled that there's no "do while" loop in go.

slackfan 2 days ago

Still better (compiler speed) than Rust.

  • gf000 2 days ago

    Still not playing remotely in the same league. Only one of them is a "systems language", reusing Go's inappropriate marketing term.

tom_m a day ago

Not only is it really good, it's even better for AI.

rand0m4r 2 days ago

This was an interesting read and very educational in my case, but each time I read an article criticizing a programming language it's written by someone who hasn't done anything better.

It's a shame because it is just as effective as pissing in the wind.

  • terminalbraid 2 days ago

    If you're saying someone can't credibly criticize a language without having designed a language themselves, I'll ask that you present your body of work of programming language criticisms so I know if you have "produced something better" in the programming language criticism space.

    Of course, by your reasoning this also means you yourself have designed a language.

    I'll leave out repeating your colorful language if you haven't done any of these things.

    • olddustytrail a day ago

      > If you're saying someone can't credibly criticize a language without having designed a language themselves

      Actually I think that's a reasonable argument. I've not designed a language myself (other than toy experiments) so I'm hesitant to denigrate other people's design choices because even with my limited experience I'm aware that there are always compromises.

      Similarly, I'm not impressed by literary critics whose own writing is unimpressive.

      • kstrauser a day ago

        Who would be qualified to judge their those critics’ writing as good or bad? Critics already qualified as good writers? Who vetted them, then? It’d have to be a stream of certified good authors all the way back.

        No, I stick by my position. I may not be able to do any better, but I can tell when something’s not good.

        (I have no opinion on Go. I’ve barely used it. This is only on the general principle of being able to judge something you couldn’t do yourself. I mean, the Olympics have gymnastic judges who are not gold medalists.)

  • kstrauser a day ago

    I’ve never been a rock star, but I think Creed sucks.

    I really don’t like your logic. I’m not a Michelin chef, but I’m qualified to say that a restaurant ruined my dessert. While I probably couldn’t make a crème brûlée any better than theirs, I can still tell that they screwed it up compared to their competitor next door.

    For example, I love Python, but it’s going to be inherently slow in places because `sum(list)` has to check the type of every single item to see what __add__ function to call. Doesn’t matter if they’re all integers; there’s no way to prove to the interpreter that a string couldn’t have sneaked in there, so the interpreter has to check each and every time.

    See? I’ve never written a language, let alone one as popular as Python, but I’m still qualified to point out its shortcomings compared to other languages.

    • ModernMech 6 hours ago

      > See? I’ve never written a language, let alone one as popular as Python, but I’m still qualified to point out its shortcomings compared to other languages.

      You kind of undermine your argument here, because someone experienced in building languages wouldn't identify this as a shortcoming of Python compared to other languages, but rather a design choice that was made to support other features; designing a language is all about making these tradeoffs to achieve specific design goals. It's one thing to keep boxed values around and be slow because of it -- it's another to do so because you want to support a dynamic type system.

      This might be what the other poster was getting at when they said they don't want to listen to non-experts here, because the criticisms are usually shallow and based on personal programming preferences rather than an actual critique of language design.

      • kstrauser 35 minutes ago

        Anyone would identify that as a shortcoming, albeit one deemed worthwhile giving the flexibility it brings to the table. It’s certainly not an advantage of the language to have to do that slow check on every operation. It’s just a downside to all the dynamism we enjoy.

        Also, I said I didn’t write a language. I never said that I didn’t maintain my own local fork so I could play with the VM and add opcodes to it and see what happens if I try experiments like “assume that all(type(_) is type(list[0])) for _ in list[i..])”, which I’ve done just for fun to see how it’d explode when that assumption fails.

        But no, I haven’t written a language of my own, I apparently I’m not qualified to have an opinion.

813ac4312b25c 2 days ago

> Probably [hello NIGHTMARE !]. Who wants that? Nobody wants that.

I don't really care if you want that. Everyone should know that that's just the way slices work. Nothing more nothing less.

I really don't give a damn about that, i just know how slices behave, because I learned the language. That's what you should do when you are programming with it (professionally)

  • thomashabets2 2 days ago

    Yup. If you code in Go then you should know that.

    Just like every PHP coder should know that the ternary operator associativity is backwards compared to every other language.

    If you code in a language, then you should know what's bad about that language. That doesn't make those aspects not bad.

    • theodorejb a day ago

      Note that since PHP 8.0 the ternary operator is non-associative, and attempting to nest it without explicit parenthesis produces a hard error.

  • skitter 2 days ago

    The author obviously knows that too, otherwise they wouldn't have written about it. All of these issues are just how the language works, and that's the problem.

  • terminalbraid 2 days ago

    If you're fine with that then you should be upset by the subsequent example, because by your own definition "that's just not the way slices work".

    • 813ac4312b25c 2 days ago

      I am fine with the subsequent example, too. If you read up about slices, then that's how they are defined and how they work. I am not judging, I am just using the language as it is presented to me.

      For anyone interested, this article explains the fundamentals very well, imo: https://go.dev/blog/slices-intro

      • terminalbraid 2 days ago

        Then you seem to be fine with inconsistent ownership and a behavioral dependence on the underlying data rather than the structure.

        You really don't see why people would point a definition that changes underneath you out as a bad definition? They're not arguing the documentation is wrong.

  • lawn a day ago

    > because I learned the language

    If that's your argument then there are no bad design decisions for any language.