Skip to content

Python-like context management #200

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
johnd0e opened this issue Mar 24, 2025 · 7 comments
Open

Python-like context management #200

johnd0e opened this issue Mar 24, 2025 · 7 comments

Comments

@johnd0e
Copy link

johnd0e commented Mar 24, 2025

As explained for example here: https://www.geeksforgeeks.org/with-statement-in-python/

As "with" keyword is not available, we could use "using" instead, like in C#

@SkyyySi
Copy link

SkyyySi commented Mar 24, 2025

That's already a thing. Declaring a variable with close (instead of local, global or const) will make its __close metamethod get called at the end of the scope or when an error occurs. For example, when running the code blow as a file /tmp/test.yue...

do
    close x = {
        <>: {
            __close: () =>
                print("Closing #{@}...")
        }
    }

    print("Foo")

    error("Bar")

... the following will show when running it:

Foo
Closing table: 0x30002dc7a0...
/tmp/test.yue:11: Bar
Stack Traceback
===============
(1) '/tmp/test.yue':11
(2) global C function 'xpcall'
(3) '=(yuescript)':110

Sidenote: This is a feature from regular Lua 5.4, where it is written as local x <close> = ... instead. However, YueScript can generate a polyfill when targeting lower Lua versions.

@johnd0e
Copy link
Author

johnd0e commented Mar 24, 2025

Good to know, thanks!
Still I miss the expressiveness of the same concept in python.

But we almost there:

with a = io.open _filename
  close f = a
  f::read "*all"

This would be more concise without helper variables:

with close f = io.open _filename
  f::read "*all"

What do you think, is this reasonable request?

@johnd0e
Copy link
Author

johnd0e commented Mar 24, 2025

P.S.
There is another issue: in Lua < 5.4 file objects do not have __close metamethod, so may be we should allow __close ?? close?

@SkyyySi
Copy link

SkyyySi commented Mar 25, 2025

Good to know, thanks!
Still I miss the expressiveness of the same concept in python.

But we almost there:

with a = io.open _filename
  close f = a
  f::read "*all"

This would be more concise without helper variables:

with close f = io.open _filename
  f::read "*all"

What do you think, is this reasonable request?

You can just do this:

do
    close f = io.open(filename)
    r::read("*a")

This also has the added advantage that you can easily use the read value by simply writing some_var = do ... instead. In YueScript, with has a different meaning already - you'd be using the file handle as the value. Of course, using an already-closed file handle doesn't really make sense. But completely changing the semantics specifically for with close ... (or whatever it would end up looking like) sounds to me like it would become a source of frustration / confusion more than anything.

P.S.
There is another issue: in Lua < 5.4 file objects do not have __close metamethod, so may be we should allow __close ?? close?

The problem here is that you'd be introducing a compatibility issue the other way around. Now, if a third-party library has ::close() but not ::__close(), then it will only be called for Lua 5.3 and below, but not 5.4.

You could consider simply putting something like this at the entrypoint of your app and avoid that headache:

do
    const f = assert(io.open("/dev/null"))
    f.<"__close"> ??= () => @close()
    f::close()

@johnd0e
Copy link
Author

johnd0e commented Mar 25, 2025

You could consider simply putting something like this at the entrypoint of your app and avoid that headache:

do
    const f = assert(io.open("/dev/null"))
    f.<"__close"> ??= () => @close()
    f::close()

It's not clear to me why would I ever need __close metamethod here, as it is not used at all

do
    close f = io.open(filename)
    r::read("*a")

I tend to agree that for now it is the best option. Still I'd find the following more expressive:

using f = io.open(filename)
    r::read("*a")

@johnd0e
Copy link
Author

johnd0e commented Apr 5, 2025

Ok, as I see using is also already has some meaning in yue, so I am changing my idea.

What if allow to write assignment in the same line as do:

do close f = io.open(filename)
  f::read("*a")

And allow close/const in other contexts, e.g.

if close f := io.open(filename)
  f::read"*a"

@SkyyySi
Copy link

SkyyySi commented Apr 6, 2025

And allow close/const in other contexts, e.g.

if close f := io.open(filename)
  f::read"*a"

+1 for this. IMO, the walrus operator should not be needed here, since the const keyword already prevents an accidental switcheroo where you actually meant to write == instead of =. It could also be generalized:

if [decl] x = something
    ...

where [decl] may be be one of local, global, const or close.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants