in reply to [perl6] Complex Attribute Validation and/or Triggers

Updated July 2015 for coherence and to note related SO qa.

Hi Dean,

First, thanks for exploring Perl 6. I hope it's fun. :)

If you use the `is rw` trait on an attribute, you're asking the compiler to automatically generate a setter for you, a method with the same name as the attribute. This method will be a lvalue routine. And the setter method syntax (eg `$i.lb = 6`) will work.

You create a custom setter method by explicitly declaring a method with the same name as the attribute. You should mark it `is rw` and return a Proxy object with suitable FETCH (read) and STORE (write) routines.

However, please know that, in addition to eluding documentation, Proxies may lead you to encounter bugs, not yet implemented stuff, missing sugar, etc.

(In the Perl 6 world, unlike in the Perl 5 world, the current implementation and especially documentation state of things is often LTA. Both the software and doc (activity) continue to improve but they've still got a long, long way to go.)

  • Comment on Re: [perl6] Complex Attribute Validation and/or Triggers

Replies are listed 'Best First'.
Re^2: [perl6] Complex Attribute Validation and/or Triggers
by duelafn (Parson) on Mar 09, 2015 at 13:26 UTC

    Indeed it is fun. I love the language and now with Inline::Perl5 the number of projects I'm willing to try it out on is greatly increased!

    Thanks for all your research and link to proxys. Indeed, it looks like these can save us if we were to create a class with a publicly writable attribute which really should have been implemented as a method. And, indeed there do seem to be bugs lurking still. I can't quite get it to work now, but am sufficiently satisfied that something like this will eventually:

    class Interval { has Real $.lb = die 'Lower bound is required'; has Real $.ub = die 'Upper bound is required'; method lb() is rw { return Proxy.new: FETCH => method () { return $!lb; }, STORE => method ($lb) { die "Require lb <= ub" unless $lb <= $!ub; $!lb = $lb; }; } method ub() is rw { return Proxy.new: FETCH => method () { return $!ub; }, STORE => method ($ub) { die "Require lb <= ub" unless $!lb <= $ub; $!ub = $ub; }; } }

    Good Day,
        Dean

      Updated with info about bug. Moved Interop section to its own post.

      Glad to hear you're having fun. :)

      if we were to create a class with a publicly writable attribute

      All attributes are private. No matter what. All public access is via public methods. Another comment covers this in more detail.

      I can't quite get it to work now:

      After some digging I've concluded that one should not use `method` as the routine declarator for the value of the FETCH and STORE arguments to the Proxy but rather `sub`:

      # This is NOT a good way to validate for this use case. # But this code works in my current Rakudo. class Interval { has $.lb = die 'Lower bound is required'; has $.ub = die 'Upper bound is required'; method lb() is rw { return Proxy.new: # note `sub` declarator and ignored arg: FETCH => sub ($) { return $!lb; }, # note `sub` declarator and ignored (first) arg STORE => sub ($, $lb) { die "Require lb <= ub" unless $lb <= $!ub; $!lb = $lb; }; } method ub() is rw { return Proxy.new: FETCH => sub ($) { return $!ub; }, STORE => sub ($, $ub) { die "Require lb <= ub" unless $!lb <= $ub; $!ub = $ub; }; } }

      Using the `method` declarator as a FETCH or STORE routine isn't useful because the self in the called routine (passed in as the first arg, corresponding to the anonymous `$` parameter in the above declarations) would not be the enclosing class.

      Update Has been discussed on #perl6. jnthn landed a fix for a couple of related bugs a few hours later. Your code still won't work though; fixing it will apparently have to wait for now.