http://qs1969.pair.com?node_id=1150090


in reply to Declaring with my, assigning, and testing in same line

First this behaviour is well documented in perlsub ¹

And if it's not a frequent use case, please use two lines.

The next maintainer will be thankful to easily read how @v is defined:

my @v= keys %h; die "Got extra Z: @v\n" if @v >1

Otherwise if you really need it that often, why don't you simply define a helper function?

DB<100> sub check { die "Got extra Z: @_\n" if @_ >1 } DB<101> %h=(a=>1) => ("a", 1) DB<102> check my @v= keys %h => "" DB<103> %h=(a=>1,b=>2) => ("a", 1, "b", 2) DB<104> check my @v= keys %h Got extra Z: a b

And if you need more flexibility in testing, use some functional magic

DB<106> sub avoid (&;@) { my $code=shift; my $msg = $code->(@_); die $msg if $msg; } DB<107> avoid { "Got extra Z: @_\n" if @_ >1 } my @v = keys %h; Got extra Z: a b DB<108> %h=(a=>1) => ("a", 1) DB<109> avoid { "Got extra Z: @_\n" if @_ >1 } my @v = keys %h; DB<110>

but again, for the sake of readability please use a line break (even if it's a one liner)

avoid { "Got extra Z: @_\n" if @_ >1 } my @v = keys %h;

And for completeness , I'm sure you could also use variable :attributes for such checks.

 my @v :check = keys %h;

See Attribute::Handlers for details.

Cheers Rolf
(addicted to the Perl Programming Language and ☆☆☆☆ :)
Je suis Charlie!

¹)

The declared variable is not introduced (is not visible) until +after the current statement. Thus, my $x = $x; can be used to initialize a new $x with the value of the old $x +, and the expression my $x = 123 and $x == 123 is false unless the old $x happened to have the value 123.

Replies are listed 'Best First'.
Re^2: Declaring with my, assigning, and testing in same line (attributes)
by choroba (Cardinal) on Dec 12, 2015 at 20:44 UTC
    And for completeness , I'm sure you could also use variable :attributes for such checks.
    It's possible, but not straightforward. Attributes alone can't do it, because they don't happen at runtime, so they don't see the assigned values:
    my @v :Nonempty = keys %h;

    Nonempty has no acces to the keys of %h. Fortunately, you can use attributes with tie. It's still not easy, though: the constructor (TIEARRAY) doesn't see the assigned values, either. STORE sees them, but it's not called when the right hand side list is empty! Fortunately, there's one method that gets called in any assignment: EXTEND:

    #!/usr/bin/perl use warnings; use strict; { package Array::Nonempty::Attr; use Attribute::Handlers; sub Nonempty :ATTR(ARRAY) { tie @{ $_[2] }, 'Array::Nonempty' } } { package Array::Nonempty; use Tie::Array; use parent -norequire => 'Tie::StdArray'; use Carp; sub EXTEND { my ($self, $size) = @_; croak "Cannot be empty" if 0 == @$self && 0 == $size; $self->SUPER::EXTEND($size) } } use parent -norequire => 'Array::Nonempty::Attr'; my %hash_ok = ( answer => 42 ); my %hash_empty = (); my @keys_ok :Nonempty = keys %hash_ok; my @keys_empty :Nonempty = keys %hash_empty;

    You can add other methods to check they wouldn't help you:

    To create an array that can never be empty, you'd have to implement SHIFT, POP, and SPLICE, too.

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
      Thanks I started experimenting myself after you messaged me.

      As said you'll need to tie cause Attributes happen at compile time and you have to intercept the assignment in STORE.

      (didn't know about EXTEND till now)

      I'm fine with using tie as long as I can untie when I'm done. Otherwise the penalty for this syntactic sugar would be to huge.

      I took a simpler case, tieing a scalar, and trying to use untie $$self within STORE {}. Didn't work. (probably because dereferencing isn't allowed for untie or scope restrictions)

      IMO as long we can't automatically untie, this approach shouldn't be used.

      update

      seems liked I didn't realy understood Tie::StdScalar yet

      update
      this approach with autotieref

      use Attribute::Handlers autotieref => { Selfish => Tie::Selfish };

      should provide the necessary reference to the tied structure, so that untieing becomes possible.

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)
      Je suis Charlie!

        You can tie in the attribute handler, so you can untie there, too. So, a closure sent as an extra argument to the constructor works:
        #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; { package Array::Nonempty::Attr; use Attribute::Handlers; sub Nonempty :ATTR(ARRAY) { my $referer = $_[2]; tie @$referer, 'Array::Nonempty', sub { untie @$referer } } } { package Array::Nonempty; use Tie::Array; use parent -norequire => 'Tie::StdArray'; use Carp; sub CLEAR { my $self = shift; $self->[0] = []; } sub TIEARRAY { my ($class, $untie) = @_; bless [ [], $untie ], $class } sub EXTEND { my ($self, $size) = @_; croak "Cannot be empty" if 0 == @{ $self->[0] } && 0 == $size; $self->SUPER::EXTEND($size); # Prevent "untie attempted while 1 inner references still exis +t" my $untie = $self->[1]; undef $self; $untie->() } } use parent -norequire => 'Array::Nonempty::Attr'; my %hash_ok = ( answer => 42 ); my %hash_empty = (); my @keys_ok :Nonempty = keys %hash_ok; say tied(@keys_ok) // 'not tied'; say for @keys_ok; my @keys_empty :Nonempty = keys %hash_empty;
        ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
        > I'm fine with using tie as long as I can untie when I'm done. Otherwise the penalty for this syntactic sugar would be to huge.

        At second thought this is a too limited technique... consider

        while(1) { my $x :check = some_call(); }

        The check would be done only once, so in this case I'd clearly prefer a functional approach

        while(1) { check {RULE} my $x = some_call(); }

        Using attributes is a pretty way to apply ties, but most use cases I can think of combining this with untie are limited to debugging.

        > > > And for completeness , I'm sure you could also use variable :attributes for such checks.

        Wrong I was, young padawan. ;-)

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!