S04 -- closure traits clarification

9 views
Skip to first unread message

David Christensen

unread,
Apr 29, 2005, 11:57:01 AM4/29/05
to perl6-l...@perl.org
Greetings,

In trying to hack closure trait support into pugs, I have some
questions about closure traits, variable with "will" traits and
introspection. (Apologies if some of this has been discussed on the
list before -- I'm just going off of the synopses, which if definite
clarification on some of these issues has been made, should probably be
updated to reflect the decisions made.)

Firstly, it is suggested in S04 that variables indicated with a "will"
predicate contribute to the corresponding block-level trait. I.e., if
we have the following bit of code:

if $dbh {
my $sth will undo {$dbh.rollback} will keep {$dbh.commit} = FIRST
{$dbh.prepare($query)};
UNDO {
say "DB error!";
}
KEEP {
say "We're good!";
}
}

Then the block has in effect 5 total traits, 2 UNDO block, 2 KEEP
blocks and 1 FIRST blocks. From what I understand, the blocks for each
trait are executed in FIFO order, thus we would rollback before we
report the error in this contrived example.

Questions:

1) What type of introspection, if any, are we providing to the language
level? I.e., are we providing something along the lines of

%traits = &?BLOCK.traits

where %traits is keyed on trait name (FIRST, LAST, whatever) and in
turn is an array of closures? This would mean, for instance that we
could say

&?BLOCK.traits<FIRST>

to get the current block's FIRST closures, if any. When parsing the
block for traits, coming across a new FIRST block would be akin to
saying:

push &?BLOCK.traits<FIRST>, {...block contents...}

Specifically, I'm looking for definition of the syntax, which is only
alluded to in the Synopsis.

2) If we accept the introspection at the block-level above, it seems
clear that we should also accept the same .traits method on variables.
I.e., in the above DBI example, we should get back the closure(s) for
undoing by referring to $sth.traits<UNDO>. Is a variable-level trait a
single entry, or can we have multiple "will undo {...}" predicates on a
single variable? (The utility of such is left as an exercise to the
reader.)

3) User-definable traits. Now, this may be a closed domain of sorts,
but do we need to allow for the possibility of user-defined traits?
(I'm thinking here of variable-level "will" predicates.) If so, do
user-defined traits get normalized to UPPER? It would seem like we
would want consistency here, because if "will undo {...}" and "UNDO
{...}" get stored in the same trait slot, we're obviously transforming
one of the identifiers -- should this behavior be specific to our
"built-in" ones, or to all traits?

4) Which of the closure traits are supported as "will" predicates on
variables? Not all of the closure traits make sense on the
variable-level -- this information will be useful when trying to parse
the "will" predicates.

Thanks,

David Christensen

Luke Palmer

unread,
Apr 29, 2005, 11:26:42 AM4/29/05
to David Christensen, perl6-l...@perl.org
David Christensen writes:
> Greetings,
>
> In trying to hack closure trait support into pugs, I have some
> questions about closure traits, variable with "will" traits and
> introspection. (Apologies if some of this has been discussed on the
> list before -- I'm just going off of the synopses, which if definite
> clarification on some of these issues has been made, should probably be
> updated to reflect the decisions made.)
>
> Firstly, it is suggested in S04 that variables indicated with a "will"
> predicate contribute to the corresponding block-level trait.

Not really. `will` is just defined as:

$a will foo {...}

Is the same as:

$a is foo({...})

So it's up to foo to associate itself with the block.

> I.e., if we have the following bit of code:
>
> if $dbh {
> my $sth will undo {$dbh.rollback} will keep {$dbh.commit} = FIRST
> {$dbh.prepare($query)};
> UNDO {
> say "DB error!";
> }
> KEEP {
> say "We're good!";
> }
> }
>
> Then the block has in effect 5 total traits, 2 UNDO block, 2 KEEP
> blocks and 1 FIRST blocks. From what I understand, the blocks for each
> trait are executed in FIFO order, thus we would rollback before we
> report the error in this contrived example.

Nope. Entry-time blocks are executed in declaration order. Exit-time
blocks are executed in reverse declaration order. Just like CHECK and
END in Perl 5.

> Questions:
>
> 1) What type of introspection, if any, are we providing to the language
> level? I.e., are we providing something along the lines of
>
> %traits = &?BLOCK.traits
>
> where %traits is keyed on trait name (FIRST, LAST, whatever) and in
> turn is an array of closures? This would mean, for instance that we
> could say
>
> &?BLOCK.traits<FIRST>
>
> to get the current block's FIRST closures, if any. When parsing the
> block for traits, coming across a new FIRST block would be akin to
> saying:
>
> push &?BLOCK.traits<FIRST>, {...block contents...}
>
> Specifically, I'm looking for definition of the syntax, which is only
> alluded to in the Synopsis.

What you are saying here seems reasonable. However, we have to remember
that the closures are different for each run. So the traits are not
associated with the block, but with the particular runtime instance of
the block. Maybe that's what &?BLOCK refers to anyway.

I wonder how you could talk about the traits of a sub that isn't
currently executing.

> 2) If we accept the introspection at the block-level above, it seems
> clear that we should also accept the same .traits method on variables.
> I.e., in the above DBI example, we should get back the closure(s) for
> undoing by referring to $sth.traits<UNDO>. Is a variable-level trait a
> single entry, or can we have multiple "will undo {...}" predicates on a
> single variable? (The utility of such is left as an exercise to the
> reader.)

The question is, are you asking about the variable or the value? For
instance:

my $block = &?BLOCK; # _not_ calling
$block.traits; # variable traits or &?BLOCK traits?

We probably just use whatever the equivalent of `tied` is these days.
Let's call it, er, `tied`.

$block.traits; # &?BLOCK traits
(tied $block).traits; # variable traits

> 3) User-definable traits. Now, this may be a closed domain of sorts,
> but do we need to allow for the possibility of user-defined traits?

No. By which I mean OF COURSE!

> (I'm thinking here of variable-level "will" predicates.) If so, do
> user-defined traits get normalized to UPPER?

No. Block level traits are different from variable traits, and they
should be declared separately. The variable trait `undo` for instance
probably just pushes an UNDO handler on its caller. Likewise, the UNDO
macro (?) does precisely the same thing.

But people are allowed to declare traits that are all caps, and block
handlers which are lowercase, and any combination of the above. Perl
culture will try to enforce against that, however.

> It would seem like we would want consistency here, because if "will
> undo {...}" and "UNDO {...}" get stored in the same trait slot, we're
> obviously transforming one of the identifiers -- should this behavior
> be specific to our "built-in" ones, or to all traits?
>
> 4) Which of the closure traits are supported as "will" predicates on
> variables? Not all of the closure traits make sense on the
> variable-level -- this information will be useful when trying to parse
> the "will" predicates.

Hmm, not quite sure. Traits are a pretty big thing, and I'm not sure
what it buys you to hack them in. I'd start by implementing the block
handlers without variable traits.

Luke

Larry Wall

unread,
May 2, 2005, 6:20:03 PM5/2/05
to perl6-l...@perl.org
On Fri, Apr 29, 2005 at 10:57:01AM -0500, David Christensen wrote:
: 1) What type of introspection, if any, are we providing to the language
: level? I.e., are we providing something along the lines of
:
: %traits = &?BLOCK.traits
:
: where %traits is keyed on trait name (FIRST, LAST, whatever) and in
: turn is an array of closures? This would mean, for instance that we
: could say
:
: &?BLOCK.traits<FIRST>
:
: to get the current block's FIRST closures, if any.

There is no .traits method. Traits store their values into properties,
and properties are just mixins, and you get at the properties by calling
methods. These particular traits push onto a correspondingly named
property, so you're probably looking for

$?BLOCK.firstlist

or some such.

: When parsing the

: block for traits, coming across a new FIRST block would be akin to
: saying:
:
: push &?BLOCK.traits<FIRST>, {...block contents...}
:
: Specifically, I'm looking for definition of the syntax, which is only
: alluded to in the Synopsis.

Probably does something like:

&?BLOCK does First; # no-op if it already does First
&?BLOCK.firstlist.push(&block);

: 2) If we accept the introspection at the block-level above, it seems

: clear that we should also accept the same .traits method on variables.
: I.e., in the above DBI example, we should get back the closure(s) for
: undoing by referring to $sth.traits<UNDO>. Is a variable-level trait a
: single entry, or can we have multiple "will undo {...}" predicates on a
: single variable? (The utility of such is left as an exercise to the
: reader.)

The variable might not be aware that it has had such a trait applied
to it. Remember that traits are allowed to cheat. These traits need
only arrange to have &block pushed onto the appropriate list after the
corresponding variable is successfully elaborated at run time, or otherwise
arrange not to run if their variable is not yet elaborated. If this
information also shows up as metadata on the variable, that's probably
okay, but the implementation is unlikely to use that property directly.

Of course, a trait like "constant" would be expected to an effect on
the variable itself.

: 3) User-definable traits. Now, this may be a closed domain of sorts,

: but do we need to allow for the possibility of user-defined traits?
: (I'm thinking here of variable-level "will" predicates.) If so, do
: user-defined traits get normalized to UPPER? It would seem like we
: would want consistency here, because if "will undo {...}" and "UNDO
: {...}" get stored in the same trait slot, we're obviously transforming
: one of the identifiers -- should this behavior be specific to our
: "built-in" ones, or to all traits?

Already specced in A12. As far as I know that part hasn't changed
significantly, but that could be because nobody's tried to implement
it yet. :-)

: 4) Which of the closure traits are supported as "will" predicates on

: variables? Not all of the closure traits make sense on the
: variable-level -- this information will be useful when trying to parse
: the "will" predicates.

As Luke mentioned, "will" is just syntactic sugar for "is". A trait
is allowed to care about its semantics at compile time when you call
the trait handler, but this is totally transparent to the parser,
which will happily accept "will qufklzuxfvklj {...}", turn it into
"is qufklzuxfvklj({...})", and then fail to find an appropriate trait
handler in semantic analysis, or find some default trait handler that
scratches its head over "qufklzuxfvklj" before admitting defeat.

Or it may be that any trait that is unrecognized just gets applied as
a property of the variable. But I think it's better if we treat
traits as declared items so that we catch typos, unless someone has
explicitly declared a autoloading trait handler.

Larry

Larry Wall

unread,
May 2, 2005, 6:41:12 PM5/2/05
to perl6-l...@perl.org
On Mon, May 02, 2005 at 03:20:03PM -0700, Larry Wall wrote:
: Probably does something like:

:
: &?BLOCK does First; # no-op if it already does First
: &?BLOCK.firstlist.push(&block);

Probably shouldn't use up a normal name like "First" for that. Maybe we
can just reuse the trait name as the role name

&?BLOCK does FIRST; # no-op if it already does First
&?BLOCK.FIRST.push(&block);

In this view, whether setting a trait clobbers an existing property or
merely appends to it depends on the trait handler's implementation.

I'm inclining more toward the view that all these blocks are pushed onto
the FIRST property at BEGIN time, but the "will first" properties are
wrapped in code that prevents them from running before their variable
is declared. (Or maybe the wrapper just checks to see if the variable
is defined? That would disable the block when you undef the variable.
That might be construed as a feature.)

Larry

Reply all
Reply to author
Forward
0 new messages