Skip to content
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

add deferScoped, library defined scoped defer #16048

Closed
wants to merge 1 commit into from

Conversation

timotheecour
Copy link
Member

@timotheecour timotheecour commented Nov 19, 2020

A lot of code (in compiler, stdlib, nimble) is incorrect wrt exception handling as mentioned here https://forum.nim-lang.org/t/4022#44758

Typical example is code that looks correct but isn't because author is unaware that something might raise (eg os.sameFileContent is actually buggy since readBuffer can raise, as I mentioned in https://github.com/nim-lang/Nim/pull/16008/files#r524822306), or worse, might be correct today but become incorrect after some unrelated change in the future. defer prevents this entire class of bug, and does so without adding (sometimes large) lexical scopes.

however there's some pushback for using defer more widely in compiler sources https://forum.nim-lang.org/t/4022#44770

Great update! However, I still don't like to see defer in the Nim compiler code and I dislike it so much that I thought of adding a switch --araqstyle (which forbids defer and continue) that is enabled for the compiler's code.

and there are legitimate cases where defer in a scope is preferable.

This PR adds a library defined scoped defer. The semantic differences wrt defer have been explained in #16010 and this in no way makes builtin defer (aka D's scope(exit)) obsolete; just different use cases, with, yes, some overlap.

example

when true:
    from std/strutils import contains
    let a = open(currentSourcePath)
    deferScoped: close(a) # immediately follows code that needs cleanup
    do:
      doAssert "deferScoped" in readAll(a)

links

why not just use try/finally?

as explained in nim-lang/RFCs#236 (comment)

which syntactically improves a bit over try/finally because the cleanup code immediately follows the code that needs cleaning up

notes

@timotheecour timotheecour changed the title add defers.deferScoped add defers.deferScoped, library defined scoped defer Nov 19, 2020
@timotheecour timotheecour changed the title add defers.deferScoped, library defined scoped defer add deferScoped, library defined scoped defer Nov 19, 2020
@timotheecour timotheecour force-pushed the pr_deferSCoped branch 2 times, most recently from f55cd69 to 7c53157 Compare November 19, 2020 07:04
Comment on lines +8 to +9
deferScoped: close(a) # immediately follows code that needs cleanup
do:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh please. This is even worse than defer. There is no visual indication that the do: block belongs to the deferScoped.

Copy link
Member Author

@timotheecour timotheecour Nov 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Araq

There is no visual indication that the do: block belongs to the deferScoped.

you don't have to use do in the common case of 1-liner cleanup code, eg this works, and shows that visual indication:

echo "before"
deferScoped(echo "cleanup"):
  echo "inner"

echo "before"
deferScoped(echo "cleanup"):
  echo "inner"

but do is part of the language and users should learn how it works (and you could use same argument for other API's using do)

The whole point is it doesn't escape the template/macro scope it's called from, unlike defer

template fn():
  prelude()
  deferSCoped: cleanup()
  do:
    inner()

fn()
# `cleanup` called before the next statement
fn()
# `cleanup` called before the next statement

@Araq
Copy link
Member

Araq commented Nov 19, 2020

I'm seriously sorry but no. Upcoming PRs should simply use try finally.

@Araq Araq closed this Nov 19, 2020
@Araq
Copy link
Member

Araq commented Nov 19, 2020

The cleanup / close should be paired with the open call, deferScoped doesn't do that. As a compromise we can use something like withFile or withX or anything resembling Python here. And please notice that Python's with does not repeat defer's weaknesses IMO, it connects the creation and destruction steps properly.

@Araq
Copy link
Member

Araq commented Nov 19, 2020

Heck, I like Python's with statement so much better that I would add it to Nim and deprecate defer. Speaking theoretically here, we're past v1 and all that, we don't need the schism.

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

Successfully merging this pull request may close these issues.

2 participants