As a few weeks ago, going through the Python ideas forum has introduced me again to another interesting idea. As usual, someone proposes a syntax for a feature, and as it's clear that no syntax changes will be performed to provide that, people come up with interesting work arounds.
So someone proposed allowing to use with, the syntax for Automatic Resource Management with context managers, as an expression (so having a with expression along with the existing with statement. He was proposing something like this:
txt = do_something(json.load(f) with open('foo.json') as f)
That indeed reminds me of the syntax outlined in the rejected PEP for exception-catching expressions (aka try-expressions):
msg = (parse(txt) except ParsingError: None)
As I said, it's obvious that given how reluctant the Python leaders are to any syntax change, neither of those ideas will ever make it into the language. The good thing is that same as we can easily define a do_try function as the one we saw in this previous post, we can also define a using/with_do function, like this (taken from the discussion thread):
#def with_do(mgr, fn):
def using(mgr, fn):
with mgr as res:
return fn(res)
# or maybe this is more semantic?
def do_with(fn, mgr):
with mgr as res:
return fn(res)
#config = tomllib.load(with open("file.toml", "rb") as f: f)
config = using(open("file.toml", "rb"), tomllib.load)
config = do_with(tomllib.load, open("file.toml", "rb"))
#data = with open("file.txt", "r") as f: f.read()
data = using(open("file.txt", "r"), lambda f: f.read())
data = do_with(lambda f: f.read(), open("file.txt", "r"), )
All the above examples are pretty contrived, as "with open() as" can be replaced by pathlib.Path.read_text, that takes care of managing any exception. I mean:
config = tomlib.loads(pathlib.Path('foo.json').read_text())
But there are other context manager use cases for which this kind of function would come handy.
The other Automatic Resource Management (ARM) mechanisms I'm familiar with are the C# using statement with IDisposables and Java Try-with-resources statement with Closables. So in Python, C# and Java ARM is provided via statements, not expressions, that's why I think I had never thought of using it as an expression. Given that in Kotlin almost everything is an expression, is easy to imagine that they have had this into account. Kotlin does not have a specific syntax construct for ARM, as given its rich and expressive syntax it can be nicely implemented with an extension function of the Closable interface, use. It Executes the given block function on this resource (a Closable object) and then closes it down correctly whether an exception is thrown or not:
inline fun T.use(block: (T) -> R): R
As you can see in the signature, the block returns a value R, that in turn is returned by use, so what if we just want to execute a block that does not return anything? Well, that signature is also valid. In Kotlin a function that does not return anything does indeed return Unit (a singleton class), so when passing to use a block that does not return anything that generic type R becomes Unit, and everything is perfectly valid.
No comments:
Post a Comment