in reply to Re^13: Assignable Subroutines
in thread Assignable Subroutines

I feel silly having to demonstrate such trivial solutions to someone who otherwise seems quite knowledgable, but here goes:

#!/usr/bin/perl use strict; use warnings; package Tie::EnsureLowercaseSubstr; use Carp; sub TIESCALAR { my $class = shift; my %self; @self{ qw( strref offs len ) } = @_; bless \%self, $class; } sub STORE { my $self = shift; my ( $value ) = @_; croak 'Bad value' unless $value =~ /^[a-z]+$/; substr( ${ $self->{ strref } }, $self->{ offs }, $self->{ len }, $ +value ); } sub FETCH { my $self = shift; substr( ${ $self->{ strref } }, $self->{ offs }, $self->{ len } ); } package SillyClass; sub new { my $class = shift; my ( $value ) = @_; bless \$value, $class; } sub modifySubstring: lvalue { my( $self, $start, $length ) = @_; tie my $response, 'Tie::EnsureLowercaseSubstr', \$$self, $start, $ +length; $response; } package main; my $silly = SillyClass->new( 'a teststring' ); $silly->modifySubstring( 3, 3 ) = 'ABC';

Part two:

#!/usr/bin/perl use strict; use warnings; package Tie::EvenUniqueTrueArray; use Tie::Array; use base qw( Tie::StdArray ); use Carp; my( %Array, %Start, %End ); sub TIEARRAY { my $class = shift; my ( $array, $start, $end ) = @_; my $self = [ @{ $array }[ $start .. $end ] ]; ( $Array{ 0 + $self }, $Start{ 0 + $self }, $End{ 0 + $self } ) = +( $array, $start, $end ); bless $self, $class; } sub STORE { my $self = shift; my ( $idx, $value ) = @_; $self->[ $idx ] = $value; my %seen; croak 'Bad value' if !$value or ( $value & 1 ) or grep $seen{ $_ }++, @$self; } sub DESTROY { my $self = shift; @{ $Array{ 0 + $self } }[ $Start{ 0 + $self } .. $End{ 0 + $self } + ] = @$self; delete $_->{ 0 + $self } for \( %Array, %Start, %End ); } package SillyClass2; sub new { my $class = shift; bless \@_, $class; } sub modifySubset: lvalue { my( $self, $start, $end ) = @_; tie my @response, 'Tie::EvenUniqueTrueArray', \@$self, $start, $en +d; @response; } package main; my $silly = SillyClass2->new( map $_ * 2, 1 .. 50 ); ( $silly->modifySubset( 15, 16 ) ) = ( 20, 22 ); ( $silly->modifySubset( 15, 35 ) ) = ( 1 .. 20 );

All of that was all there in my previous reply.

Goes to show why it's nice that Perl6 will only require a fraction of the red tape, though.

Makeshifts last the longest.

Replies are listed 'Best First'.
Re^15: Assignable Subroutines
by BrowserUk (Patriarch) on Jan 27, 2005 at 02:39 UTC

    Now imagine that the LVALUE sub below (with better syntax) was a builtin and the TIESCALAR was invisible--generated automatically by the compiler.

    #! perl -slw use strict; package Sensible; sub TIESCALAR { my( $class, $fetch, $store ) = @_; no warnings 'redefine'; *FETCH = *FETCH = $fetch; *STORE = *STORE = $store; bless [], $class; } sub LVALUE (&&) : lvalue { my( $fetch, $validate ) = @_; tie my $lvalue, 'Sensible', $fetch, $validate; $lvalue; } sub new { my( $class, $init ) = @_; return bless \$init, $class; } sub attr : lvalue { my( $self, $start, $len ) = @_; $start ||= 0; $len ||= length ${ $_[ 0 ] }; LVALUE { substr( $$self, $start, $len ) } sub{ warn( 'Bad value' ), return unless $_[ 1 ] =~ m[^[a-z ]+$]; substr( $$self, $start, $len ) = $_[ 1 ]; }; } package main; my $sensible = Sensible->new( 'The quick brown fox jumps over the lazy + dog' ); print $sensible->attr; $sensible->attr( 10, 5 ) = 'green'; print $sensible->attr; $sensible->attr( 20 ) = 'did not see the paint tin'; print $sensible->attr; $sensible->attr( 10, 5 ) = 'ORANGE'; print $sensible->attr; __END__ [ 2:03:26.03] P:\test>425402-2 The quick brown fox jumps over the lazy dog The quick green fox jumps over the lazy dog The quick green fox did not see the paint tin Bad value at P:\test\425402-2.pl line 31. The quick green fox did not see the paint tin

    Note how there is no need for a separate tie class for every different validation routine. Just one for scalars, one for arrays, and one for hashes.

    Note how all the code is in a single place. And how the validation code has full access to the entire environment of the original sub. No need to pass everything required, it is all available via closure.

    One additional keyword and some (a little) extra code generated--by the compiler, not the programmer.

    As you can see, you needn't feel silly. I was there a long time ago.


    Examine what is said, not who speaks.
    Silence betokens consent.
    Love the truth but pardon error.

      That doesn't work if you have several different scalar properties in the same class which require different forms of validation, nor does it work if the same lvalue is accessed multiple times at once (a trivially constructible circumstance).

      Makeshifts last the longest.

        It is a P5 simulation, without the benefit of P6 lexical subroutines. How do you think this will work?

        class ....{ my sub thingie1 is rw { return my $var is Proxy( for => $hidden_var, FETCH => { ... }, STORE => { ... }, TEMP => { ... }, ... ); } my sub thingie2 is rw { return my $var is Proxy( for => $hidden_var, FETCH => { ... }, STORE => { ... }, TEMP => { ... }, ... ); } ... }

        And, if the ties involved in the my suggestion above, are generated by the compiler, what is to stop them from using the same mechanism as this Apo6 code uses to isolate the tie for one attribute from the tie for another?

        The only difference is who writes the code and where. One is inline, clear and trivial with the compiler doing all the drudge work.

        The other is, out-of-band, complex, obscure and repetative, requiring the programmer to code lots of little code blocks instead of a simple, inline block of code.


        Examine what is said, not who speaks.
        Silence betokens consent.
        Love the truth but pardon error.
Re^15: Assignable Subroutines
by BrowserUk (Patriarch) on Jan 27, 2005 at 02:08 UTC

    And you would trade all that for the simplicity of the commented out code?

    I rest my case!


    Examine what is said, not who speaks.
    Silence betokens consent.
    Love the truth but pardon error.

      Please show how you would propose to validate values for four attributes, each in a different class, but validated in the same way, using some approach that assumes something like the commented out code worked.

      Makeshifts last the longest.

        It would depend upon 2 things:

      • How complex is the validation criteria
      • What, if any relationship is there between the four classes/attributes

        Perhaps the best test of the relationship is:

      • If the validation criteria changes for one of the attributes, will it change for all of them? Always?

        If the answer to that question is yes, which implies that the four attributes represent the same entity in the four classes, then it wold make sense to factor that code into a common place.

        Maybe as a utility subroutine called from the inline validation code. But then again, if they are all the same thing, maybe they should be a separate class? That would I guess depend upon whether they have any other behaviours besides existing and requiring validation.

        However, if these are completely unrelated fields that just happen to require similar validation criteria--as, perhaps, required to be lower case--then I would not use a common validation routine.

        To do so leaves you open to someone changing the validation routine to accomodate future changes in one field and unwittingly breaking the other three unrelated classes.

        In either case, whether done manually, or through a subroutine, the best place for it is right there after the value is assigned.


        Examine what is said, not who speaks.
        Silence betokens consent.
        Love the truth but pardon error.