I wrote:
> David L. Nicol wrote:
> > sub DirectBubbleSort() {
> >   my ($i,$t) = (-1,0);
> >   while (++$i <= $#$__) {
> >     $$__[$i] > $$__[1+$i] and $t++ and @$__[$i,1+$i] = @$__[1+$i,$i];
> >   };
> >   $t and @$__ = DirectBubbleSort;
> > }
> >
> > @SomeList = DirectBubbleSort; # instead of DirectBubbleSort(\@SomeList)
> >
>
> I don't see what the problem with DirectBubbleSort(\@SomeList) is. It's
> rather explicit, and is very clear about what is done. Using a (\@)
> prototype on DirectBubbleSort would be ok too, since its name says right
> what it does and everybody likes some vanilla syntax. But making it
> @SomeList = DirectBubbleSort is the most error-prone thing I see! Nobody
> will get it right on first use...


Changed my mind...

Actually, I really reconsidered it. It really is useful (for another thing
other than bubblesorting its return value)! If we prohibit reading the
variable with $__ and allow setting it only with return, this would be
actually useful to get around a current prototype inconsistence.

First, let me say what I believe is a inconsistence in prototypes in Perl 5.

Before prototypes were added, if I wanted to pass an array as an array to a
sub, I had to pass a reference to it:

    sub my_sub {
        my $array = shift;
        $array->[0] = 1;
    };
    my_sub \@my_array;

With prototypes, I could give my_sub a \@ prototype, and could call it
without the reference:

    sub my_sub (\@) {
        my $array = shift;
        $array->[0] = 1;
    };
    my_sub @my_array;

Now suppose I want to return an array by reference. There are actually two
good reasons to do that: one is return a tied array/hash, and the other is
to avoid useless copy of data. Suppose I have:

    sub my_sub {
        my @array;
        for (5..7) {
            push @array, $_;
        }
        return @array;
    }
    @my_array = my_sub;

That would copy the values 5, 6, 7 to Perl stack and then push them into
@my_array. Now suppose I have

    sub my_sub {
        my @array;
        for (5..5004) {
            push @array, $_;
        }
        return @array;
    }
    @my_array = my_sub;
    $my_first_element = $my_array[0];

That would copy 5000 values to Perl stack, only to copy them back to
@my_array later. This could be solved with references, just the way it was
used to pass an array as a parameter:

    sub my_sub {
        my @array;
        for (5..5004) {
            push @array, $_;
        }
        return \@array;
    }

Now I have two ways of calling it:

    $my_array_ref = my_sub;
    $my_first_element = $my_array_ref->[0];

This is bad since it breaks all @my_array uses, you would have to replace
$my_array[$x] with $my_array_ref->[$x] in all subsequent cases. The other
possibility is:

    *my_array = my_sub;
    $my_first_element = $my_array[0];

This doesn't break @my_array uses, but it breaks all assignment to my_sub,
having to use a glob to do the assignment. This is also bad because the glob
doesn't make it clear what is the thing that's being assigned to: a scalar,
an array, or a hash.

Contrasting this example with C++, we can clearly see that it's a prototype
issue. In C++, the passing of the array by parameter to the sub by reference
without and with prototypes could be seen as

    void myFunction1(Array *byPointer);
    void myFunction2(Array &byReference);

    Array a();

    myFunction1(&a);
    myFunction2(a);

As C++ explicitly returns its return type, you can do:

    Array *myFunction1();
    Array &myFunction2();

    Array *a;
    Array b(NULL);

    a = myFunction1();
    b = myFunction2();   // this actually calls the copy-constructor, but
                         // in Perl it shouldn't be necessary.


What I propose to be able to not need to say ``*my_array = my_sub'' is to
flag my_sub with a special attribute that would work like a return
prototype. It could even be added to my_sub's prototype, say separe with a
colon between the input and return prototypes:

    sub my_sub :would_alias { ...
    sub my_sub (:\@)        { ...

And then this:

    @my_array = my_sub;

would behave (be compiled) like this:

    *my_array = my_sub;

my_sub could then be changed to return the values more efficiently with
transparency for who calls it. And it would be possible to define functions
that return tied objects that get aliased to the return of the function.

Additionally, the \@ prototype could be used for return in the same method:

    sub my_sub :alias {
        my @array;
        for (5..5004) {
            push @array, $_;
        }
        return @array;
    }

This way, the sub body wouldn't have to change, and it would work both with
and without `:alias', only one way more efficiently than the other.

Comments?





Problems:
    Suppose @a and @b are aliases, so that @a = (1, 2, 3) would make @b ==
3.
    What does @a = my_sub does? Both @a and @b are aliased to the result of
my_sub, or only @a?

    A little experience shows that this is confusing in Perl 5 too. Try:

        *a = \$b;
        $a = "foo\n";
        print $b;        # prints foo
    ------------------------------------------
        *a = \$b;
        *c = \$b;
        $a = "foo\n";
        print $c;        # prints nothing
    ------------------------------------------
        *a = \$b;
        *b = \$c;
        $a = "foo\n";
        print $c;        # also prints nothing
    ------------------------------------------
        *b = \$a;
        *b = \$c;
        $a = "foo\n";
        print $c;        # also prints nothing
    ------------------------------------------
        *c = \$b;
        *b = \$a;
        $a = "foo\n";
        print $c;        # also prints nothing

    I guess that demonstrates that aliasing is a wild beast and using it for
more than two variables is probably a good way to get in trouble...

- Branden

Reply via email to