develooper Front page | perl.perl5.porters | Postings from August 2009

A complete design for := (bind)

Thread Next
Chip Salzenberg
August 20, 2009 22:44
A complete design for := (bind)
Message ID:
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.


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.


   :($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.


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.


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" }


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 Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About