Closed dumblob closed 2 years ago
I love context managers and even prefer them over "defer" keywords. But they also has its own disadvantages, specially nesting two or three of them (in Python you can use a comma-separated syntax, but still that consumes a lot of horizontal space).
I was discussing that another day with some friends, but we couldn't come to any conclusion on the subject. I don´t want to force developers to indent more their code, you know, or even end procedures with sequences like }}}}}
(but in the end I think there's no escape...).
One implementation idea is to be smart with names, like matching file.open
with file.close
instead of taking the Python approach of having an object containing and defining everything.
with (file.open "/etc/hostname") as f {
do_something $f
}
The with
command already has the result of file.open
and in the end pass it as argument to file.close
. That would work fine, but still has the }}}
potential problem.
The real problem here is: I don't think it's okay to have a command inside a SimpleList. That just doesn't feel right. Ideally, commands for not immediate execution should live inside SubLists, but in this case it's important to have only one being called and easily identifiable.
Maybe it's the case to wait for that syntax that allow creating and passing around Commands
. Or, in the worst scenario, simply say with f being file.open "/etc/hostname"
, as ugly as that may be...
Many (about 30%) of with ...
places in my Python code deals with more than one context managers. This should be kept in mind when designing the syntax.
Btw. how about combining defer
+ with
into one command. Basically a "predefined defer
" like:
set @f (file.open "/etc/hostname")
do_something $f
With the meaning: set variable f
to a file handle and install (@
prefix) the handle's __defer
(__exit__()
in Python terms) method to the end of the current scope.
Or something like that (yeah, set
seems to become the ultimate tool for everything :wink: - we should carefully design it's syntax to be extensible for future types & use cases).
One idea is:
proc read_file (path) {
set f [scoped file.open $path]
...
return $content
}
So scoped
(a regular command) is able to analyze the command being passed to it and automatically call the opposite one (file.close
) when the current scope is gone.
(For a more "traditional" context manager, it's probably better to create some kind of "local scope" command that simulates the behavior of starting and closing a new one.)
set path "/etc/os-release"
context {
set f [scoped file.open $path]
set content [read $f]
}
io.out $f
# closed file handler
io.out $content
# The file content
Perfect, scoped
and context
sound good to me. Thanks!
Btw. scoped
could make things easier if it optionally accepted a "guard" (as Dao does) - it saves lots of boilerplate and makes the code more readable.
Well, now we have both scope
and autoclose
, and they work fine, so I'm closing this issue.
Great!
Before trying to carve out some special built-in syntax or commands or whatever, it's usually good to try to think the problem in terms of nested context managers. It appears many problems from our world can be modeled like that (incl. error handling, etc.).
I don't have any specific command nor syntax in mind, but it'd make sense to give it a thought before diving into extending the language. Maybe the language grammar/spec could stay quite minimal thanks to this.
I'm also thinking about implementing context managers with compile-time macros. But that's all for discussion.