Hacker News new | ask | show | jobs
by topspin 500 days ago
Regarding the statements on golang's defer:

  "the defer call is hoisted to the outside of the for loop in func work"
Astonishing. Add that to the list of golang head scratchers. That is one of the biggest "principle of least astonishment" violations I've ever seen.

Disclaimer: Not a golang hater. Great language. Used it myself on occasion, although I remain a golang neophyte. Put away the sharp objects.

5 comments

I love the Principle of Least Astonishment, but I first encountered it in the Ruby book and I gave up reading it halfway through because I kept thinking, "He and I have very different definitions of astonishing..."
In a way that makes sense though, once you're at a level where you can not only write Ruby, but write books about Ruby, surely very few things would astonish you.
That's why I always watch the newbies squirm trying to read my/our documentation.

No feedback is ever as honest as unvarnished confusion.

It's definitely a footgun but I think it's also pretty clear in go docs that defer is a function-return-time thing, versus a loop iteration thing. "A defer statement defers the execution of a function until the surrounding function returns." from https://go.dev/tour/flowcontrol/12

I think per scope-level is probably better, but honestly still - as a I mention elsewhere - still something that seems fairly limited compared to writing code inside blocks that clean themselves up in the Ruby world. The more we're messing with scope, the more it seems like it would be possible to go all the way to that? The go-style defer appears likely to be simpler from an implementation POV; if we're gonna make it harder let's go all the way!

I know a lot of people hate the nesting of indentation from that, but it makes so many other things harder to screw up.

My biggest astonishment is how people continue to shoot themselves in the foot by not making scope vs function declarations explicit. For the reasons that “someone will misunderstand complicated ideas, so let’s make it implicit” or something. While there could be just:

  defer x // scope scoped
  defer fn x // function scoped
Also:

  var a = 0
  fn var a = 0
  for fn i := …
But we have this allergy to full control and invent these increasingly stupid ways to stay alert and get unpleasantly surprised anyway.

Edit: Same for iifes. Everyone uses them, so turn these into proper embedded blocks!

I wouldn't want my life turned into an embedded block, whatever that is.
Say that the defer would execute inside for loops, what would make you more astonished: loops and functions are the exceptions, or defers execute at the end of any block? I would prefer the latter of these two. But then the consequence is that a defer in an if-block executes instantly, so you cannot conditionally defer anymore. So it seems that the rules for when deferees execute need to be arbitrary, and "only functions" seems fewer exceptions than "only functions and loops", isn't it? And what about loops implemented through gotos? Oh boy.
> so you cannot conditionally defer anymore.

I think you can, for example this way:

- declare a function pointer variable cleanup - initialize it with no_op - call defer ‘call cleanup’ - if, inside a block, you realize that you want to do something at cleanup, set cleanup to another function

That’s more code, but how frequent is it that one wants to do that?

One thing this doesn’t support is having a call into third party code defer cleanup to function exit time. Does golang supports that?

> a defer in an if-block executes instantly, so you cannot conditionally defer anymore.

Of course you can, using ?: (or && and || if you prefer), just like any other case where you want an expression rather than a statement. Or simply using the non-block form of if. (Some stupid autoformatters or tech leads insert extraneous braces, but you should be avoiding those already).

The parent I was replying to was talking about Go. There is no ternary operator in Go. There are no non-block forms of if in Go. I'm not sure what your "of course" is referring to, but I guess it is unrelated.
I figured we were talking about C since that's the main topic. In any case short-circuit && and || still work in Go AIUI, so my point stands.
It's incredibly ugly but you could sort of hack in a smaller-scoped defer using anonymous functions: https://go.dev/play/p/VgnprcObPHz
Yeah, I get it. There's an idiom. Still, that glitch is guaranteed to catch everyone off guard, experienced or otherwise, when taking up golang. As I said, it's an entry on the list, and such a list exists for most (all?) mainstream languages. At least it's minor compared to nil, a flaw somehow promulgated in a brand new language many years after anyone purporting to be a language designer would or should have known to avoid. That's a mystery for the ages right there.