Front page | perl.perl5.porters |
Postings from August 2009
A complete design for := (bind)
Thread Next
From:
Chip Salzenberg
Date:
August 20, 2009 22:44
Subject:
A complete design for := (bind)
Message ID:
20090821054433.GD16532@tytlal.topaz.cx
Not long ago, I posted a patch that began implementing the ':=' bind
(sometimes also called "alias") operator. That patch implemented the
seemingly obvious cases:
my $x := <any_scalar_value>;
my @x := @any_array;
my %y := %any_hash;
Subsequent discussion revealed that _list_ binding raised much more thorny
language issues than the above. I asked Patrick Michaud how Perl 6 does it
(FSVO "does"), and got this summary (paraphrased):
The LHS of := must be a Signature object, and the RHS must be a Bind
(whether the Bind is an object, I am not clear). The syntax of a Bind
is C<\LIST>. There is some thought being given to making the syntax of
Signatures C<:LIST>. Finally, the semantics of ":=" and function
parameter binding are identical, except that parameters default to
readonly while ":=" does not.
I believe this is a basically sound design, which we can follow fairly
closely in parts (and transpose liberally in others). This is my plan to
bring both the mountain and Muhammad to a mutually agreed rendezvous.
PROBLEMS AND SOLUTIONS ON THE LHS
Perl 6 can easily use lists of things in ways that Perl 5 can't, because Perl
6 has a marker ("*") to mean "all elements of @a". With that marker, the
array stands for its elements; in its absence, the array stands for itself.
In contrast, Perl 5's flattening is almost entirely contextual, with only ()
and C<scalar> to provide some incomplete control. Adding C<scalar> on the
LHS of binds would clustter them badly, and would also be inconsistent with
the historical meaning of the operator. If only we could add the splat to
Perl 5... but that would only help if we could make no-flatten the default,
which is just plain impossible. In general.
Fortunately, the Signature syntax C<:LIST> gives us an out. Inside a
Signature -- whether a function declaration or C<:LIST> -- we can adopt the
Perl 6 default of no-flatten and use the splat as a flatten marker.
Thus:
:($a, @b, %c) := ...
expects three things on the RHS: a scalar (somehow), an array (somehow), and
a hash (somehow). In contrast,
:($a, *@b, *%c) := ...
expects only scalars (somehow) on the RHS. The first is bound to $a, the
rest are bound as individual elements of @b, and %c ends up empty.
Furthermore, it turns out we need special syntax for the LHS of binds anyway,
even without the splat problem: We need to be able to specify which binds are
readonly and, in function argument lists, we also need to specify which are
read-write and which aren't even binds. Thus, let us continue to borrow from
Perl 6:
:($a is ro) := ...
is a readonly bind, and
:($a is copy) := ...
is actually an assignment. It could have been spelled C<$a = ...> with
equal effect. (It's generally useful only in function signatures.)
In short, C<:LIST> solves many problems.
Side point: An unidentified variable inside a C<:LIST> could be
automatically declared. This question is orthogonal and, IMO, not deeply
interesting; we can work it out later, once the rest is functional.
PROBLEMS AND SOLUTIONS ON THE RHS
Let us begin with the simplest solution that will work: Take as the RHS of :=
a list of references to values of appropriate types.[1] Perl 5 has adequate,
if slightly clunky, syntax to do what's needed here. For example, to bind
arrays per se:
:(@a, @b, @c) := (\@x, \@y, \@z);
Meanwhile, to bind array elements rather than whole arrays:
:(@a) := \(@x);
Now we could introduce special syntax for the RHS, but that would be hard and
probably unnecessary. Sure it'd be nice to use the same splat rules on the
RHS. But we would have to invent an entirely novel syntax marker to enable
it -- we can't use the Perl 6 C<\LIST> because Perl 5 already has a meaning
for that. In any case, there's no point in being inventive on the RHS until
we have a working LHS.
WHAT ABOUT BIND WITH FUNCTIONS?
It should work eventually, but we don't really *need* to make it work until
we have lexical functions. Until then we have glob manipulation. Someday,
though, I expect this to work:
:(my &lexifunc) := \&some_function;
And therefore these would be practically synonymous someday:
:(my &foo) := sub { say "hi" };
my sub foo { say "hi" }
WHAT ABOUT THE REALLY SIMPLE BIND SYNTAX THAT I ALREADY IMPLEMENTED?
I see no need for it. It doesn't give us any substantial convenience; Occam
suggests that it should go unless it can defend itself; and while binding
will be very frequent, the explicit := operator will be quite rare. Saving a
few parentheses isn't worth the effort.
And, there we are.
[1] As usual in a language spec, we follow the "as-if" rule: Perl is to act
"as if" everything written here is true. Perl is allowed to take shortcuts
when they are invisible. For example, it's possible -- even likely -- that
the statement
:(my *@args) := \(@_);
or the equivalent
sub foo (*@args) { ... }
will not actually create a reference value object for each element of @_,
only to throw them all away. But Perl will, nevertheless, act _as_if_ it
did just that.
--
Chip Salzenberg
Thread Next
-
A complete design for := (bind)
by Chip Salzenberg