develooper Front page | perl.perl5.porters | Postings from June 2021

Re: RFC 0004 - defer {} syntax

Thread Previous | Thread Next
From:
Sergey Aleynikov
Date:
June 17, 2021 09:16
Subject:
Re: RFC 0004 - defer {} syntax
Message ID:
CAKNj8S39Gx1PBC23xDL7=UyDRRcvh3de5gyw-dtBE2ueBD3NYQ@mail.gmail.com
There's already a set of modules on CPAN
(https://metacpan.org/pod/Guard,
https://metacpan.org/pod/Scope::Guard, others) doing the same thing
using a scope_guard/guard verb. So, I have a number of questions:

- why invent a new verb for already existing semantic?
- what do all current CPAN implementations lack, thus requiring this feature?
- why has this to be a core feature, compared to yet another CPAN module?
- why any existing module can't be bundled with Perl, achieving this
functionality?

Best regards,
Sergey Aleynikov

чт, 17 июн. 2021 г. в 01:15, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>:
>
> Here's an RFC formalization of my currently-open github feature issue:
>
>   https://github.com/Perl/perl5/issues/17949
>
> which was itself an issue tracking the previous mailing list
> conversation:
>
>   https://www.nntp.perl.org/group/perl.perl5.porters/2020/06/msg257611.html
>
> If we accept this one into the RFC process, I'll likely close the
> github issue and point at the RFC instead.
>
> ---
>
> # defer {} syntax
>
> ## Preämble
>
>     Author:  Paul Evans <PEVANS>
>     Sponsor:
>     ID:      0004
>     Status:  Draft
>
> ## Abstract
>
> Add a new control-flow syntax written as `defer { BLOCK }` which
> enqueues a block of code for later execution, when control leaves its
> surrounding scope for whatever reason.
>
> ## Motivation
>
> Sometimes a piece of code performs some sort of "setup" operation, that
> requires a corresponding "teardown" to happen at the end of its task.
> Control flow such as exception throwing, or loop controls, means that
> it cannot always be written in a simple style such as
>
>   {
>     setup();
>     operation();
>     teardown();
>   }
>
> as in this case, if the `operation` function throws an exception the
> teardown does not take place. Traditional solutions to this often rely
> on allocating a lexical variable and storing an instance of some object
> type with a `DESTROY` method, so this runs the required code as a result
> of object destruction caused by variable cleanup. There are no in-core
> modules to automate this process, but the CPAN module `Scope::Guard` is
> among the more popular choices.
>
> It would be nice to offer a native core syntax for this common
> behaviour. A simple `defer { BLOCK }` syntax removes from the user any
> requirement to think about storing the guard object in a lexical
> variable, or worry about making sure it really does get released at the
> right time.
>
> As well as being more convenient for users, it would help decouple Perl
> core's object behaviours, like `DESTROY`, from this task. If less code
> relies on DESTROY, it may one day be possible to change the way that
> part of the code works, without worrying so much about code that relies
> on it breaking.
>
> This syntax has already been implemented as a CPAN module,
> [`Syntax::Keyword::Defer`](https://metacpan.org/pod/Syntax::Keyword::Defer).
> This RFC formalizes an attempt implement the same in core.
>
> ## Rationale
>
> ((TODO - I admit I'm not very clear on how to split my wording between
> the Motivation section, and this one))
>
> The name "defer" comes from a collection of other languages.
> Near-identical syntax is provided by Swift, Zig, Jai, Nim and Odin. Go
> does define a `defer` keyword that operates on a single statement,
> though its version defers until the end of the containing function, not
> just a single lexical block. I did consider this difference, but ended
> up deciding that a purely-lexical scoped nature is cleaner and more
> "Perlish", overriding the concerns that it differs from Go.
>
> ## Specification
>
> A new lexically-scoped feature bit, requested as
>
>   use feature 'defer';
>
> enables new syntax, spelled as
>
>   defer { BLOCK }
>
> This syntax stands alone as a full statement; much as e.g. a `while`
> loop does. The deferred block may contain one or multiple statements.
>
> When the `defer` statement is encountered during regular code
> execution, nothing immediately happens. The contents of the block are
> stored by the perl interpreter, enqueued to be invoked at some later
> time, when control exits the block this `defer` statement is contained
> within.
>
> If multiple `defer` statements appear within the same block, the are
> eventually executed in LIFO order; that is, the most recently
> encountered is the first one to run:
>
>   {
>     setup1();
>     defer { say "This runs second"; teardown1(); }
>
>     setup2();
>     defer { say "This runs first"; teardown2(); }
>   }
>
> `defer` statements are only "activated" if the flow of control actually
> encounters the line they appear on (as compared to `END` phaser blocks
> which are activated the moment they have been parsed by the compiler).
> If the `defer` statement is never reached, then its deferred code will
> not be invoked:
>
>   while(1) {
>     defer { say "This will happen"; }
>     last;
>     defer { say "This will *NOT* happen"; }
>   }
>
> ((TODO: It is currently not explicitly documented, but naturally falls
>   out of the current implementation of both the CPAN and the in-core
>   versions, that the same LIFO stack that implements `defer` also
>   implements other things such as `local` modifications; for example:
>
>   our $var = 1;
>   {
>     defer { say "This prints 1: $var" }
>
>     local $var = 2;
>     defer { say "This prints 2: $var" }
>   }
>
>   I have no strong thoughts yet on whether to specify and document -
>   and thus guarantee - this coïncidence, or leave it unsaid.))
>
> ## Backwards Compatibility
>
> The new syntax `defer { BLOCK }` is guarded by a lexical feature guard.
> A static analyser that is not aware of that feature guard would get
> confused into thinking this is indirect call syntax; though this is no
> worse than basically any other feature-guarded keyword that controls a
> block (e.g. `try/catch`).
>
> For easy compatibility on older Perl versions, the CPAN implementation
> already mentioned above can be used. If this RFC succeeds at adding it
> as core syntax, a `Feature::Compat::Defer` module can be created for it
> in the same style as
> [`Feature::Compat::Try`](https://metacpan.org/pod/Feature::Compat::Try).
>
> ## Security Implications
>
> None foreseen.
>
> ## Examples
>
> A couple of small code examples are quoted above. Further, from the
> docs of `Syntax::Keyword::Defer`:
>
>   use feature 'defer';
>
>   {
>     my $dbh = DBI->connect( ... ) or die "Cannot connect";
>     defer { $dbh->disconnect; }
>
>     my $sth = $dbh->prepare( ... ) or die "Cannot prepare";
>     defer { $sth->finish; }
>
>     ...
>   }
>
> ## Prototype Implementation
>
> CPAN module `Syntax::Keyword::Defer` as already mentioned.
>
> In addition, I have a mostly-complete branch of bleadperl (somewhat
> behind since I haven't updated it for the 5.34 release yet) at
>
>   https://github.com/leonerd/perl5/tree/defer
>
> I'm not happy with the way I implemented it yet (don't look at how I
> abused an SVOP to store the deferred optree) - it needs much tidying up
> and fixing for the various things I learned while writing the CPAN
> module version.
>
> ## Future Scope
>
> If this RFC becomes implemented, it naturally follows to enquire
> whether the same mechanism that powers it could be used to add a
> `finally` clause to the `try/catch` syntax added in Perl 5.34. This
> remains an open question: while it doesn't any new ability, is the
> added expressive power and familiarity some users will have enough to
> justify there now being two ways to write the same thing?
>
> ## Rejected Ideas
>
> On the subject of naming, this was originally called `LEAVE {}`, which
> was rejected because its semantics don't match the Raku language
> feature of the same name. It was then renamed to `FINALLY {}` where it
> was implemented on CPAN. This has been rejected too on grounds that
> it's too similar to the proposed `try/catch/finally`, and it shouldn't
> be a SHOUTY PHASER BLOCK. All the SHOUTY PHASER BLOCKS are declarations,
> activated by their mere presence at compiletime, whereas `defer {}` is
> a statement which only takes effect if dynamic execution actually
> encounters it. The p5p mailing list and the CPAN module's RT queue both
> contain various other rejected naming ideas, such as UNWIND, UNSTACK,
> CLEANUP, to name three.
>
> Another rejected idea is that of conditional enqueue:
>
>   defer if (EXPR) { BLOCK }
>
> as this adds quite a bit of complication to the grammar, for little
> benefit. As currently the grammar requires a brace-delimited block to
> immediately follow the `defer` keyword, it is possible that other ideas
> - such as this one - could be considered at a later date however.
>
> ## Open Issues
>
> Design-wise I don't feel there are any remaining unresolved questions.
>
> Implementation-wise the code still requires some work to finish it off;
> it is not yet in a merge-ready state.
>
> ## Copyright
>
> Copyright (C) 2021, Paul Evans.
> This document and code and documentation within it may be used,
> redistributed and/or modified under the same terms as Perl itself.
>
> --
> Paul "LeoNerd" Evans
>
> leonerd@leonerd.org.uk      |  https://metacpan.org/author/PEVANS
> http://www.leonerd.org.uk/  |  https://www.tindie.com/stores/leonerd/

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About