Link: Parsing: a timeline
Jeffery Kegler's Parsing: a timeline (via) is what it says on the title; it's an (opinionated) timeline of various developments in computer language parsing. There are a number of fascinating parts to it and many bits of history that I hadn't known and I'm glad to have read about. Among other things, this timeline discusses all of the things that aren't actually really solved problems in parsing, which is informative all by itself.
(I've been exposed to various aspects of parsing and it's a long standing interest of mine, but I don't think I've ever seen the history of the field laid out like this. I had no idea that so many things were relatively late developments, or of all of the twists and turns involved in the path to LALR parsers.)
Go and the pragmatic problems of having a Python-like
In a comment on my entry on finalizers in Go, Aneurin Price asked:
So there's no deterministic way to execute some code when an object goes out of scope? Does Go at least have something like Python's "with" statement? [...]
For those who haven't seen it, the Python
with statement is used
with open("output.txt", "w") as fp: ... do things with fp ... # fp is automatically closed by the # the time we get here.
with gives you reliable and automatic cleanup of
or whatever resource you're working with inside the
Your code doesn't have to know anything or do anything; all of the
magic is encapsulated inside
with and things that speak its
Naturally, Go has no equivalent; sure, we have the
it's not anywhere near the same thing. In my opinion this is
the right call for Go, because of two issues you would have
if you tried to have something like Python's
with in Go.
The obvious issue is that you would need some sort of protocol to handle
initialization and cleanup, which would be a first for Go. You need the
protocol because a big point of Python's
with is that it magically
handles everything for you without you having to remember to write any
extra code; it's part of the point that using
with is easier and
shorter than trying to roll your own version (which encourages people to
use it). If you're willing to write extra code, Go has everything today
in the form of
But beyond that there is a broader philosophical issue that's exposed by Aneurin Price's first question. In a language like Go where your local data may escape into functions you call, what does it mean for something to go out of scope? One answer is that things only go out of scope when there's no remaining reference to them. Unfortunately I believe that this is more or less impossible to implement efficiently without either going to Rust's extremes of ownership tracking in the language or forcing a reference counting garbage collector (where you know immediately when something is no longer referenced). This leaves you with the finalizer problem, where you're not actually cleaning up the resource promptly.
The other answer is that 'going out of scope' simply means 'execution reaches the end of the relevant block'. As in Python, you always invoke the cleanup actions at this point regardless of whether your resource may have escaped into things you've called and thus may still be alive somewhere. This implicit, hidden cleanup is a potentially dangerous trap for your code; if you forget and pass the resource to something that retains a reference to it, you may get explosions (much) later when that now-dead resource is used. If you're in luck, this use is deterministic so you can find it in tests. If you're unlucky, this use only happens in, say, an error path.
defer() instead of an implicit cleanup doesn't stop this
problem from happening, but it makes explicit what's going on. When
you write or see a
defer(fp.Close()), you're pointedly reminded
that at the end of the function, the resource will be dead. There
is no implicit magic, only explicit actions, and hopefully this
creates enough warning and awareness. Given Go's design goals, being explicit here as part of the language
design makes complete sense to me. You can still get it wrong,
but at least the wrongness is more visible.
(I don't think being explicit is necessarily better in general than
Python's implicit magic. Go and Python are different languages with
different goals; what's appropriate for one is not necessarily
appropriate for the other. Python has both language features and
cultural features that make
with a good thing for it.)