develooper Front page | perl.perl6.language | Postings from May 2001

re: properties

From:
Damian Conway
Date:
May 18, 2001 18:30
Subject:
re: properties
Message ID:
200105190130.LAA99158@indy05.csse.monash.edu.au

Thanks to everyone who has been contributing to the discussion on
properties, and especially to those who have emailed me off-list about
the issue.

Unfortunately, I'm currently not able to respond individually to
further messages on this topic. My latest Conway Channel diary entry at
<http://www.yetanother.org/damian/diary_latest.html> explains why.
Please accept my apologies.

As my final word on the subject (for the present at least ;-), and in the
interest of trying to resolve some of the issues raised, let me once
more attempt to clearly summarize the intent and usage of properties.

I obviously didn't do an adequate job the first time.

Damian

-----------cut-----------cut-----------cut-----------cut-----------cut----------

A property is a sticky-note that you can attach to a variable (permanently,
at compile-time) or to a value (transiently, at run-time). It is possible that
there are really two entirely separate concepts there that might warrant
distinct syntaxes, but Larry is still pondering that, so let's ignore that
possibility for the moment.

Sticky notes are useful for attaching messages to things. At
compile-time they're handy for attaching messages to variables and
subroutines. Those messages are usually for the compiler, telling it to
change the implementation or behaviour of the referent in some way:

        my $foo is constant;
        sub bar is lvalue memoized {...}

But they can also be for the user's later use. For example: attaching a
note explaining the purpose of a subroutine, or allowing the user to
query whether a variable is modifiable:

        use Annotations;

        sub bar (HASH $drinker is rw) is lvalue memoized Purpose("holy place")
        {
                unless ($drinker.constant) {
                        $drinker{quaff}++ while $drinker.conscious;
                }
        }

Sticky notes attached to tools (i.e. subroutines) and containers (i.e.
variables) generally say something about the tool or container itself.
As such, they don't change once they're set.

But it's also possible to attach sticky notes to the things a tool
produces or the things a container stores. So properties can also be
used at run-time to attach "out-of-band data" to values.

Again, the messages may be for the interpreter ("this value is
true, even though it doesn't look like it"), or for the user ("this
value is undef because the gorkulator borked"):

        sub gorkulator {
                ...
                return $result is true if defined $result;
                return undef is Because($borked);
        }

Here, the C<true> property is being set I<on the value> in $result and that
annotated value is then being returned. We might want to do this because
we know the value could be 0 or "" or "0", but we'd still like to be
able to write:

        if ($res = gorkulator) {
                ...
        }

If we have to return C<undef>, we want to attach a sticky note explaining
why the subroutine failed:

        if ($res = gorkulator) {
                ...
        }
        else {
                die $res.Reason;
        }

To set a run-time property on a value, we use the 'is' operation
(just as we use 'is' to set compile-time properties on variables):

        $bar is Open;
        $bar is Open("from 5pm");
        $bar{soom} is Open("from 5pm");
        "bar" is Open("from 5pm");
        1 is Open("from 5pm");

Note that in the first three of the above cases, it's the I<value in the
variable> -- not the variable itself -- that has the C<Open> sticky-note
attached to it.

An C<is> operation returns its left argument, so they can be chained
(all the properties are attached to the same value), and can be inlined
in a <return> statement or an assignment:

        sub bar {
                ...
                return $bar is Open is Smoking("non") is Theme($theme);
        }

        my $name = "Damian" is Mispelled("often");

The C<is> is actually optional wherever it can be inferred, which is handy
for chained properties:

        my $bar is Persistent Public Logged;
        ...
        return $bar is Open Smoking("non") Theme($theme);

and can also improve the euphony (eulecty???) of code:

        return undef Because($borked);

        while (<$ARGS prompts("nuqneH? ")>) {...}


Because the C<is> notation returns the object, not the property, there is
another syntax for getting back what's I<on> the sticky note.
That syntax is the same as the method call syntax:

        $venue = bar();
        print "This is a ", $venue.Smoking, "smoking ", $venue.Theme, " bar\n";

        if ($name.Mispelled eq 'often') { ... }

Furthermore, if you call these methods with a value, that value replaces the
property's current value (which is then returned):

        $oldtheme = $venue.Theme("cigar");
        $oldsmoke = $venue.Smoking("");

Since both methods and property accessors use the same notation, the obvious
question is, what if we have a reference to an object with a C<Theme> method,
I<and> we ascribe a C<Theme> property to that reference?  What does:

        $objref.Theme()
        
do then?

The answer is that methods always override property accessors of the same name.
So the C<Theme> method is called. This is a Good Thing too, because it means
that it's possible to create synthetic, compound, or computed properties:

        sub UNIVERSAL::Dangerous {
                return @_[0].Smoking || @_[0].Theme eq 'Hell Club';
        }

or to override existing property behaviours for certain types of objects:

        sub Secret::Purpose {
                die "If I told you its purpose, I'd have to kill you";
        }

and perhaps even builtin types:

        sub SCALAR::Purpose ($value) {
                return "This scalar value's purpose is $value.prop{Purpose}"
        }

As the last example shows, within such overriding methods, there has to
be some way of accessing the actual property we're repackaging. We do
that via the builtin C<prop> property, which returns a reference to a
hash containing all the properties of a value or variable:

        $foo = 1 is Number("loneliest") Heard("ever");

        print $foo.Number;              # prints "loneliest"
        print $foo.prop{Number};        # ditto

        print keys $foo.prop;           # prints "NumberHeard"
        print values $foo.prop;         # prints "loneliestever"


You may also be wondering what happens if a variable and the value it
contains both have a property of the same name. The answer is that we
always get back the variable's property in preference to the value's:

        my $var is Purpose("var demo") = 1 is Purpose("val demo");

        print $var.Purpose;     # prints "var demo", not "val demo"

To be sure of getting the value's property instead, we simply I<evaluate>
the variable first:

        print (+$var).Purpose;  # prints "val demo", not "var demo"



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