←back to thread

Go is still not good

(blog.habets.se)
644 points ustad | 2 comments | | HN request time: 0s | source
Show context
torginus ◴[] No.44983258[source]
I still don't understand why defer works on function scope, and not lexical scope, and nobody has been able to explain to me the reason for it.

In fact this was so surprising to me is that I only found out about it when I wrote code that processed files in a loop, and it started crashing once the list of files got too big, because defer didnt close the handles until the function returned.

When I asked some other Go programmers, they told me to wrap the loop body in an anonymus func and invoke that.

Other than that (and some other niggles), I find Go a pleasant, compact language, with an efficient syntax, that kind of doesn't really encourage people trying to be cute. I started my Go journey rewriting a fairly substantial C# project, and was surprised to learn that despite it having like 10% of the features of C#, the code ended up being smaller. It also encourages performant defaults, like not forcing GC allocation at every turn, very good and built-in support for codegen for stuff like serialization, and no insistence to 'eat the world' like C# does with stuff like ORMs that showcase you can write C# instead of SQL for RDBMS and doing GRPC by annotating C# objects. In Go, you do SQL by writing SQL, and you od GRPC by writing protobuf specs.

replies(7): >>44983266 #>>44983314 #>>44983343 #>>44983484 #>>44984652 #>>44985794 #>>44992509 #
gwd ◴[] No.44983343[source]
So sometimes you want it lexical scope, and sometimes function scope; For example, maybe you open a bunch of files in a loop and need them all open for the rest of the function.

Right now it's function scope; if you need it lexical scope, you can wrap it in a function.

Suppose it were lexical scope and you needed it function scope. Then what do you do?

replies(5): >>44983468 #>>44983547 #>>44983606 #>>44983706 #>>44985635 #
torginus ◴[] No.44983706[source]
I never wanted function-scope defer, not sure what would be the usecase, but if there was one, you could just do what the other comments suggested.
replies(1): >>44986667 #
hnlmorg ◴[] No.44986667[source]
Really? I find the opposite is true. If I need lexical scope then I’d just write, for example

  f.Close() // without defer 
The reason I might want function scope defer is because there might be a lot of different exit points from that function.

With lexical scope, there’s only three ways to safely jump the scope:

1. reaching the end of the procedure, in which case you don’t need a defer)

2. A ‘return’, in which case you’re also exiting the function scope

3. a ‘break’ or ‘continue’, which admittedly could see the benefit of a lexical scope defer but they’re also generally trivial to break into their own functions; and arguably should be if your code is getting complex enough that you’ve got enough branches to want a defer.

If Go had other control flows like try/catch, and so on and so forth, then there would be a stronger case for lexical defer. But it’s not really a problem for anyone aside those who are also looking for other features that Go also doesn’t support.

replies(2): >>44988160 #>>44993549 #
1. desolate_muffin ◴[] No.44988160{4}[source]
I don't write Go, but reading this, it feels like you don't actually need functional scoping at all. In Java, I would simply write:

try (SomeResource foo = SomeResource.open()) { method(foo); }

or

public void method() { try(...) { // all business logic wrapped in try-with-resources } }

To me it seems like lexical scoping can accomplish nearly everything functional scoping can, just without any surprising behavior.

replies(1): >>44989088 #
2. hnlmorg ◴[] No.44989088[source]
Minor nit but the second example is ostensibly still just functional scoping (as in not literally but more pragmatically) because you don’t contain any branching outside of the try block within your method.

But that’s a moot point because I appreciate it’s just an example. And, more importantly, Go doesn’t support the kind of control flows you’re describing anyway (as I said in my previous post).

A lot of the comments here about ‘defer’ make sense in other languages that have different idioms and features to Go. But they don’t apply directly to Go because you’d have to make other changes to the language first (eg implementing try blocks).