5 ms·
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
by gwd 1y ago
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?
- gf000 1y agoMaking it lexical scope would make both of these solvable, and would be clear for anyone reading it. You can just introduce a new scope wherever you want with {} in sane languages, to control the required behavior as you wish.
- lowmagnet 1y agoYou can start a new scope with `{}` in go. If I have a bunch of temp vars I'll declare the final result outside the braces and then do the work inside. But lately days I'll just write a function. It's clearer and easier to test.
- tgv 1y agoCurrently, you can write if (some condition) { defer x() } When it's lexically scoped, you'd need to add some variable. Not that that happens a lot, but a lexically scoped defer isnt needed often either.
- iainmerrick 1y agoWhat's an example of where you'd need to do that? I can't recall ever needing that (but that might just be because I'm used to lexical scoping for defer-type constructs / RAII).
- tgv 1y agoSomeone already replied, but in general when you conditionally acquire a resource, but continue on failing. E.g., if you manage to acquire it, defer the Close() call, otherwise try to get another resource. Another example I found in my code is a conditional lock. The code runs through a list of objects it might have to update (note: it is only called in one thread). As an optimization, it doesn't acquire a lock on the list until it finds an object that has to be changed. That allows other threads to use/lock that list in the meantime instead of waiting until the list scan has finished. I now realize I could have used an RWLock...
- atombender 1y agoI frequently use that pattern. For example, something like this: a := Start() if thingEnabled { thing := connectToThing() defer thing.Close() a.SetThing(thing) } a.Run(ctx)
- raluk 1y agoHaving lexicial scope it is same as -> defer fn{if(some condition) x() }() within scope.
- tgv 1y agoExcept 'some condition' may change, can be long, or expensive, so you likely need an extra variable.
- masklinn 1y ago> Suppose it were lexical scope and you needed it function scope. Then what do you do? Defer a bulk thing at the function scope level, and append files to an array after opening them.
- biztos 1y agoThat seems like more work, and less readability, than sticking in the extra function. Would be nice to have both options though. Why not a “defer” package?
- anonymoushn 1y agodefer { close all the files in the collection } ?
- gwd 1y agoOK, what happens now if you have an error opening one of those files, return an error from inside the for loop, and forget to close the files you'd already opened?
- tczMUFlmoNk 1y agoYou put the files in the collection as you open them, and you register the defer before opening any of them. It works fine. Defer should be lexically scoped.
- torginus 1y agoI 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.
- hnlmorg 1y agoReally? 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.
- desolate_muffin 1y agoI 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.
- hnlmorg 1y agoMinor 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).
- connicpu 1y agoYou do what the compiler has to do under the hood: at the top of the function create a list of open files, and have a defer statement that loops over the list closing all of the files. It's really not a complicated construct.