range over func
A few days ago, Russ Cox published a proposal for range over func in Go.
Given my deep interest in iteration (slides, code & full speaker notes are available here), I had to take a look.
At first, it struck me as a bit odd.
If you want to have a look yourself, take a look at the proposed spec.
I’ll just show an example - a Go equivalent of Python’s range
“function”:
|
|
Which, translated to Python, would be
|
|
Which is quite a mouthful. It works by having the Go compiler convert a loop:
|
|
Into a function call:
|
|
With some interesting semantics:
- When breaking from the loop in any way (be it
break
,return
, orgoto
), the generatedyield
function returnsfalse
to signal the iterator function to stop; - The iterator function should return
true
if iteration completed successfully, orfalse
if it was stopped; - Any call to
yield
would trigger another iteration of the loop.
That last part is of particular interest - it means that the iterator function can completely ignore break
, and keep iterating indefinitely.
Context Managers in Go
While that last part felt wrong to me (as it can cause bugs that take forever to discover), it also felt a bit familiar.
It took me some time, playing with the code, before it clicked!
This guarantee that the iterator function must return before leaving the loop is akin to Python’s context managers.
Looking at the Go iterator function structure, you can see some similarities to context-managers written with contextlib.contextmanager
(I know, I know. But it looked similar to me.)
The main difference being that unlike in Python, in Go we also control iteration (yes, if any of you want “context managers that can re-run the code”, push for this feature in Go).
Examples
with file(…):
This means that I can finally write context-managers in Go, and get rid of that terrible (sorry Gophers) create-and-defer-close pattern that’s everywhere!
|
|
with Transaction(…):
If we want to go beyond that, we can have context managers for transactions (commit on successfully leaving the loop, cancel on panic
):
|
|
|
|
with suppress(…):
Or emulate Python’s contextlib.suppress
context-manager (despite it being entirely un-Go-like):
|
|
|
|
Go and play!
I don’t know if this feature and design will make it into Go or not. Even if it does, I don’t know what patterns of use will emerge. All that said - I think being able to write Python-style context-managers in Go is worth playing around with, and I hope more people would experiment with it!