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

Pre-RFC: Phaser Expressions

Thread Next
From:
Paul "LeoNerd" Evans
Date:
November 15, 2021 11:47
Subject:
Pre-RFC: Phaser Expressions
Message ID:
20211115114722.64c9e64a@shy.leonerd.org.uk
TL;DR: Add `BEGIN expr...` syntax to hoist the evaluation time of an
  expression into BEGIN time, without creating a new scope.

Regular `my` variable assignment happens at runtime, even though the
variable itself is visible much earlier. Usually we don't notice this
problem, but occasionally awkward things happen.

For example, in a unit test or self-contained script file, it's common
to put little helper classes at the end of the file so they don't
clutter up the main logic. This means that assignment statements in
that package happen "too late" for the program to work properly. E.g.
consider

  #!/usr/bin/perl
  use v5.36;

  Helper::say_hello;

  package Greeter {
    my $message = "Hello, world";
    sub say_hello { say $message }
  }

Here, while the variable is known to exist and thus the code compiles
OK, the assignment hasn't actually happened yet and so `undef` is
printed, with a warning.

The first reaction to this might be to wrap the line in a BEGIN block,
but then the variable is hidden by the braces of that BEGIN block.
Often this is solved in practice by splitting the declaration and
assignment in two:

  package Greeter {
    my $message; BEGIN { $message = "Hello, world"; }
    ...

This is a bit messy, and also a DRY failure - we've named the variable
twice.

It would be great if `BEGIN` (and by extension the admittedly
less-useful phasers of INIT, CHECK and UNITCHECK) could also be
prefixes for expressions, allowing one to write:

  package Greeter {
    BEGIN my $message = "Hello, world";
    sub say_hello { say $message }
  }

There are no extra braces here, so the variable isn't hidden by the
block but is visible to subsequent code. But being prefixed by BEGIN we
can see it is evaluated at BEGIN time and its side-effects (namely, the
assignment of a value into the variable) have already happened.


Phaser expressions in non-void context would also be evaluated once, at
the appropriate time, and replaced by a compiletime constant containing
the result. This might also be handy for many situations where
currently folks `use constant ...` to get a constant that's only ever
used once.

  use Digest::MD5 'md5_hex';

  foreach ( 1 .. 100 ) {
    say "The MD5 hash is ", BEGIN(md5_hex("my content here"));
  }

This would act like an "anonymous scalar", being roughly equivalent to:

  use Digest::MD5 'md5_hex';

  my $md5; BEGIN { $md5 = md5_hex("my content here"); }

  foreach ( 1 .. 100 ) {
    say "The MD5 hash is ", $md5;
  }

except without polluting the lexical namespace with an additional name.


Such syntax would also handily solve the problem of `my` being used for
class data, as discussed by:

  https://github.com/Ovid/Cor/issues/44#issuecomment-968818780

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk      |  https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/  |  https://www.tindie.com/stores/leonerd/

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