Sweetest Accessor?

X

Xiong Changnian

I'm learning, so I'm trying different idioms. Perhaps in the end it
doesn't matter but I've been working on particularly terse idioms.

One particular demand is for object data accessor methods. There are a
lot of them and they all look somewhat alike -- sounds like a demand for
laziness. What's the shortest, "sweetest" accessor sub?

Here's what I've got (eg):

METHOD1:

sub name {
my $obj = shift;
$obj->{NAME} = shift ||
$obj->{NAME};
};

This allows both gets and puts:

$oldname = $obj->name;
$obj->name ($newname);

It still doesn't satisfy me. The needless assignment-to-self irks. Note
that it does no good to change the order of precedence so that
$obj->{NAME} is returned (without assigning it) if shift tests false; by
then it's too late and shift has clobbered the old value. So here I test
shift within the right-hand side expression before making the
assignment.

Here's another:

METHOD2:

sub name {
$_[1] ? $_[0]->{NAME} =
$_[1] : $_[0]->{NAME};
};

This avoids the needless assignment but now there's a second get of
$_[1]. Supposing that the object is more complex and that hitting the
object is more expensive than hitting the param, I'd guess this second
version is more efficient. Also, no assignment to a temp $obj. It looks
a bit odd to line break the statement after the assignment operator.

I realize that some oppose needlessly terse or obscure code but I'll ask
for forgiveness here. After all, an accessor method is simple. In the
end, it's a wash anyway; I'm just experimenting. I'll take comments on
either method shown and I'd love to see suggestions.
 
A

anno4000

Xiong Changnian said:
I'm learning, so I'm trying different idioms. Perhaps in the end it
doesn't matter but I've been working on particularly terse idioms.

One particular demand is for object data accessor methods. There are a
lot of them and they all look somewhat alike -- sounds like a demand for
laziness. What's the shortest, "sweetest" accessor sub?

Here's what I've got (eg):

METHOD1:

sub name {
my $obj = shift;
$obj->{NAME} = shift ||
$obj->{NAME};
};

This allows both gets and puts:

$oldname = $obj->name;
$obj->name ($newname);

It still doesn't satisfy me. The needless assignment-to-self irks. Note
that it does no good to change the order of precedence so that
$obj->{NAME} is returned (without assigning it) if shift tests false; by
then it's too late and shift has clobbered the old value. So here I test
shift within the right-hand side expression before making the
assignment.

Here's another:

METHOD2:

sub name {
$_[1] ? $_[0]->{NAME} =
$_[1] : $_[0]->{NAME};
};

This avoids the needless assignment but now there's a second get of
$_[1]. Supposing that the object is more complex and that hitting the
object is more expensive than hitting the param, I'd guess this second
version is more efficient. Also, no assignment to a temp $obj. It looks
a bit odd to line break the statement after the assignment operator.

I realize that some oppose needlessly terse or obscure code but I'll ask
for forgiveness here. After all, an accessor method is simple. In the
end, it's a wash anyway; I'm just experimenting. I'll take comments on
either method shown and I'd love to see suggestions.

You have noticed one problem with your accessors (multiple assignment).
Another problem is that you won't be able to assign a boolean false
value that way.

The standard get-set accessor in Perl is

sub name {
my $obj = shift;
$obj->{ name} = shift if @_;
$obj->{ name};
}

It hinges on the presence of two arguments, not on the value of the
second argument, so you can assign boolean false values, (including
undef).

For low-level read-write access I am partial to lvalue accessors
these days:

sub name : lvalue { shift()->{ name} }

These are (usually) not for the class user (because you can't check
lvalues), but for your own use when you write the class code. Give
them an initial "_" if you want to make that clear.

Anno
 
M

Mumia W.

I'm learning, so I'm trying different idioms. Perhaps in the end it
doesn't matter but I've been working on particularly terse idioms.
[...]

perldoc perltoot
perldoc Class::Struct
perldoc Class::Accessor
 
X

Xiong Changnian

Another problem is that you won't be able to assign a boolean false
value that way.

Well, I'm not sure that's a problem. I abhor boolean false values as a
matter of taste. Reserving FALSE demands that every value assigned to a
variable have a TRUE value (obviously) and I think that's good style. I
realize that someone else will be quite comfortable with, say, integer
zero values in some places and require an accessor that permits them. I
just don't.

If I ever thought I would want deliberately to set a variable inside a
complex data structure to 0, "", or undef, I'd write a distinct "clear"
method.

The standard get-set accessor in Perl is

sub name {
my $obj = shift;
$obj->{ name} = shift if @_;
$obj->{ name};
}

No offense, but that's neither short nor sweet. I particularly don't
care for mixing shift and @_ in the same sub -- visually.

Of course, a working accessor might be written in any number of ways and
perhaps there are several idioms that will all compile to equivalent
byte code or be equally efficient. My immediate goal here is terseness,
sweetness -- "niftyness", if you will. Sorry if that sounds trivial.

sub name : lvalue { shift()->{ name} }

I really, really like the idea of lvalue subs. They make calling syntax
much more symmetrical and "natural". I was scared off by dire warnings
(eg):

"WARNING: Lvalue subroutines are still experimental and the
implementation may change in future versions of Perl."

I'd welcome comments. Are lvalue subs coming or going? Will my code blow
up next release?

I would like to see what sort of Frankenstein's Monsters I can build
with lvalue subs, prototypes, and overloading. I'm sure any code I post
from that lab will raise eyebrows.


Mumia W. said:
perldoc Class::Accessor

I'm sure you mean well but you must be a get-the-job-done type. It's
useless to me to let George do it; I'm experimenting.

FWIW, here's what the indicated module's doc says it generates:

# Your foo may vary.
sub foo {
my($self) = shift;
if(@_) { # set
return $self->set('foo', @_);
}
else {
return $self->get('foo');
}
}

That is definitely not a terse idiom.

I dunno, but maybe my METHOD2 is as far as one can go in my chosen
direction:

METHOD2:

sub name {
$_[1] ? $_[0]->{NAME} =
$_[1] : $_[0]->{NAME};
};

Still, nothing can beat the lvalue sub for sheer terse beauty. If only
we could depend on it?
 
A

anno4000

Xiong Changnian said:
Well, I'm not sure that's a problem. I abhor boolean false values as a
matter of taste. Reserving FALSE demands that every value assigned to a
variable have a TRUE value (obviously) and I think that's good style. I
realize that someone else will be quite comfortable with, say, integer
zero values in some places and require an accessor that permits them. I
just don't.

If I ever thought I would want deliberately to set a variable inside a
complex data structure to 0, "", or undef, I'd write a distinct "clear"
method.

I don't think you'll be happy with that in the long run. Many fields
take values that happen to be false in the normal course of things.
Suppose you have a field "score" you want to count up and down:

if ( $won ) {
$player->score( $player->score + 1);
} else {
$player->score( $player->score - 1);
}

That would fail to change the score if the score would have reached
zero. You'd need an ugly workaround, even if you have a special method
"->clear_score" to set it to zero.
No offense, but that's neither short nor sweet.

I'm not claiming short and sweet. It's standard.
I particularly don't
care for mixing shift and @_ in the same sub -- visually.
Why?

Of course, a working accessor might be written in any number of ways and
perhaps there are several idioms that will all compile to equivalent
byte code or be equally efficient. My immediate goal here is terseness,
sweetness -- "niftyness", if you will. Sorry if that sounds trivial.



I really, really like the idea of lvalue subs. They make calling syntax
much more symmetrical and "natural". I was scared off by dire warnings
(eg):

"WARNING: Lvalue subroutines are still experimental and the
implementation may change in future versions of Perl."

I'm not much worried by that. There seems to be little ongoing
discussion of lvalue subs. I think they are here to stay.

Anno
 
X

Xiong Changnian

Many fields
take values that happen to be false...

Well, yes; depends on your application. The stuff I'm doing now, this
doesn't usually come up. I make it *not* come up.

Please remember this is all a learning project for me; I'm going in
directions that have nothing to do with getting-the-job-done. With my
background, Perl is a novel and exciting language with amazingly
succinct ways of doing things. I'm pushing the edges of that as far as
possible.

Just to put a little contrast on that, I'm also designing, for the
benefit of some of my students, a set of relay logic gates; I've got up
to the full adder. This has been done before, of course, but perhaps not
with my particular design restrictions: all lines high or low, nothing
open or float; all coils grounded at one end. The intent is to make the
circuits 74CXX compatible and the most fundamental operations of a
binary digital computer as transparent as possible to the student
without sacrificing rigor. Obviously, I could get-the-job-done a lot
more easily.

Sorry if I seem to be banging my head deliberately against the wall.


I'd like to respond to your question but I'm not sure how; it's just a
stylistic or gut feeling. For complex subs, I like the idiom of shifting
all the params into my variables; it puts a nice little self-documenting
declaration block up top. Now that I've been fooling around awhile, I'm
starting to see the limitations and inefficiencies in this and I'm
trying things the other way. One bone I have to pick with @_ is that it
allows me to clobber the actual param, which I generally don't want to
do. Also, after a lifetime of subscripting, I'm straining now to avoid
it. On the other hand, there are elegant @_ and $_[$n] approaches.

If it makes me look like a more reasonable person, my generic new shifts
for the class, then passes @_ to init.

I'm not much worried by that. There seems to be little ongoing
discussion of lvalue subs. I think they are here to stay.

Well, I *really* appreciate that assurance. I'm going to take your
comment as a license to go hog wild. The lvalue accessor you posted may
well be the shortest distance between two points -- and looks good in
the calling context. Throw in prototypes and a little overloading and I
can imagine writing some lucid code in the caller. Now, if I could only
figure out how to get rid of those superfluous commas....

Of course, it's all fake and heaven help the intrepid who use my modules
without reading the docs. But then, I don't expect my stuff to be on
CPAN any time soon.

Thanks again.
 
A

anno4000

Xiong Changnian said:
Well, yes; depends on your application. The stuff I'm doing now, this
doesn't usually come up. I make it *not* come up.

Well, your question came around as being about the style of general-
purpose accessors. I would not recommend a general-purpose accessor
that doesn't transfer certain values.

[snip]
I'd like to respond to your question but I'm not sure how; it's just a
stylistic or gut feeling. For complex subs, I like the idiom of shifting
all the params into my variables; it puts a nice little self-documenting
declaration block up top.

That isn't tied to shifting. The most self-documenting way of
parameter assignment would have to be:

my ( $name, $age, $occupation) = @_;

Shifting parameters off @_ is done for various reasons:

- You want to deal with each parameter individually before you
enter the body of the sub

my $name = shift || 'anonymous';
my $age= shift || 0;
my $occupation = $default_occupation[ $age];

- You want to get things out of the way and deal with the
remaining arguments uniformly

my $mode = shift;
for ( @_ ) { # ...

- In method calls, you shift off the invocant (class or object)
to make the content of @_ conform to the parameters given at
the call site;

$obj->meth( $name, $age, $occupation);

# corresponds to

my $obj = shift;
my ( $name, $age, $occupation) = @_;
Now that I've been fooling around awhile, I'm
starting to see the limitations and inefficiencies in this and I'm
trying things the other way. One bone I have to pick with @_ is that it
allows me to clobber the actual param, which I generally don't want to
do. Also, after a lifetime of subscripting, I'm straining now to avoid
it. On the other hand, there are elegant @_ and $_[$n] approaches.

These are usually restricted to small (one-line) subs. If anything
substantial goes on in the sub body, assignment of parameters to
named variables is the norm. Unless you *want* to change an argument,
or do other things that don't work on a copy.

BTW, shift() as such doesn't stop you from changing a parameter.
"
If it makes me look like a more reasonable person, my generic new shifts
for the class, then passes @_ to init.

Right, that's one of the cases I mentioned. And congratulations for
providing a separate initializer. It's all too often forgotten.

[snip lvalue, except]
the calling context. Throw in prototypes and a little overloading and I
can imagine writing some lucid code in the caller. Now, if I could only

You can't throw prototypes into an OO design. Prototypes are ignored
in method calls. In general, prototypes appear to introduce more
problems than they solve. It is true that they let you (sometimes)
design a particularly intuitive interface. Restrict prototypes to
such cases, don't use them as a matter of course.

Overloading goes a much longer way in the direction of intuitive
interfaces, but is also not without problems. In a general-purpose
class the user should be able to opt out of overloading.

Anno
 
X

Xiong Changnian

BTW, shift() as such doesn't stop you from changing a parameter.

That statement baffles me. Perhaps I'm missing something. Here's my
understanding -- not to lecture, but please tell me where I run off the
bridge.

All in all -- as I've thanked this poster before -- I like the lvalue
sub syntax if I'm going to set an actual param value. I've been
sidestepping this issue for the most part because I'm writing object
methods, which are *expected* to take an object (hash ref) as a param
and tinker with the referent.

* * *

@_ contains aliases to actual params -- the "stuff" supplied as params
by the calling context. Thus, in:

$, = " "; $\ = "\n";
$foo = 'goodstuff';
$bar = 'morestuff';
$error = dosomething($foo, $bar);
print $foo, $bar;

SUB1:
sub dosomething {
$_[0] = 'bang';
print $_[0], $_[1];
return 0;
};

The literal 'bang' clobbers the calling context's value ('goodstuff') of
$foo -- bug, feature, or sloppy coder, you decide. In essence, it's a
call-by-reference but no dereffing is expected. Output reads:

bang morestuff
bang morestuff

.... $bar is untouched.

However:

SUB2:
sub dosomething {
my $stuff = shift;
$stuff = 'bang';
print $_[0], $_[1], '\n';
return 0;
};

Now nothing is clobbered; output reads:

morestuff
goodstuff morestuff

.... because shift -- implicitly, shift(@_) -- takes on the value of
$_[0] and then makes $_[0] an alias to $bar, so:

SUB3:
sub dosomething {
my $stuff = shift;
$_[0] = 'bang';
print $_[0], $_[1], '\n';
return 0;
};

gives output:

bang
goodstuff bang

The assignment inside the sub now clobbers $bar but $foo is untouched.

This will clobber neither $foo nor $bar:

SUB4:
sub dosomething {
my $stuff = shift;
$_[1] = 'bang';
print $_[0], $_[1];
return 0;
};

Output:

morestuff bang
goodstuff morestuff

Explanation being that at the time of shift-ing, $_[1] became undef --
and stayed that way until defined by literal assignment; the alias to
$bar is lost.

BTW:

SUB5:
sub dosomething {
@_ = ('bang', 'whimper');
print $_[0], $_[1];
return 0;
};

Output:

bang whimper
goodstuff morestuff

Assigning to @_ itself, as opposed to a subscripted element, does *not*
clobber the actual params. It has to be the whole bananna:

SUB6:
sub dosomething {
@_[0,1] = ('bang', 'whimper');
print $_[0], $_[1];
return 0;
};

Output:

bang whimper
bang whimper

Array slice isn't good enough.

* * *

I read an argument against using @_ even in the case of throwaway actual
params; you don't care if they get clobbered. What if your caller
doesn't read docs?

Calling SUB1 with:

$error = dosomething('nixon', 'agnew');

raises a fatal exception because attempting to assign to $_[0] is an
attempt to assign to a literal. Calling SUB2 is fine. FWIW, even SUB4
works okay; 'nixon' is shifted off and 'agnew' ducks the bullet.

Naturally, this is no barrier to using @_ but it's a caution. ("Do I
know what I'm doing? Okay then.")

* * *

So, it's my understanding that if I consistently shift off all my
params, I *can't* clobber them. If I use $_[$n] then it's up to me
either to keep it on the right side or deliberately clobber. If I mix
shift and $_[$n] then I'd better *really* be careful because whether I
want to clobber or not, I won't get what I want unless either I get
lucky or pay close attention to what I'm doing.

Of course, I can always shift all params and then proceed to assign
freely to $_[$n] -- but then I'm just using it as a scratch variable.

I suppose I can always test something, maybe assign a default to $_[$n],
then later shift something off. But tell me honestly: If you knew I'd
done that without good and sufficient reason, would you rush to switch
off the machine when I got my fingers caught in the gears?
 
J

John W. Krahn

Xiong said:
That statement baffles me. Perhaps I'm missing something. Here's my
understanding -- not to lecture, but please tell me where I run off the
bridge.

All in all -- as I've thanked this poster before -- I like the lvalue
sub syntax if I'm going to set an actual param value. I've been
sidestepping this issue for the most part because I'm writing object
methods, which are *expected* to take an object (hash ref) as a param
and tinker with the referent.

* * *

@_ contains aliases to actual params -- the "stuff" supplied as params
by the calling context. Thus, in:

$, = " "; $\ = "\n";
$foo = 'goodstuff';
$bar = 'morestuff';
$error = dosomething($foo, $bar);
print $foo, $bar;

[ snip ]
However:

SUB2:
sub dosomething {
my $stuff = shift;
$stuff = 'bang';
print $_[0], $_[1], '\n';
return 0;
};

Now nothing is clobbered; output reads:

morestuff
goodstuff morestuff

No, the output *should* read:

morestuff \n
goodstuff morestuff

... because shift -- implicitly, shift(@_) -- takes on the value of
$_[0] and then makes $_[0] an alias to $bar, so:

SUB3:
sub dosomething {
my $stuff = shift;
$_[0] = 'bang';
print $_[0], $_[1], '\n';
return 0;
};

gives output:

bang
goodstuff bang

It *should* look more like:

bang \n
goodstuff bang


I take it you didn't test this stuff? :)



John
 
D

Dr.Ruud

Xiong Changnian schreef:
The literal 'bang' clobbers the calling context's value ('goodstuff')
of $foo -- bug, feature, or sloppy coder, you decide. In essence,
it's a call-by-reference but no dereffing is expected.

You should stop making such statements until you understand why it is
like it is.
But even if you don't, I won't notice it, goodbye.
 
A

anno4000

Xiong Changnian said:
That statement baffles me. Perhaps I'm missing something. Here's my
understanding -- not to lecture, but please tell me where I run off the
bridge.

Well, it's true, and not stranger than the existence of lvalue subs,
but it's harder to demonstrate than I thought. I had intended to delete
that sentence and dodge an explanation, but since I let it stand,
here goes.

The result of shift() is an alias to the list element that is shifted
off and allows write access. This can be seen subs like

sub x1 { $_ = 13 for shift }

sub x2 { my $ref = \ shift; $$ref = 13 }

which both set their argument to 13 if they can.

sub x3 { shift() = 13 }

doesn't compile (probably a good thing), but in the light of the
other two that restriction is artificial.

Anno
 
X

Xiong Changnian

John W. Krahn said:
I take it you didn't test this stuff? :)

I started writing code in MT-NewsWatcher, then decided indeed to switch
to BBEdit, code and test before blatting my opinions in *.perl.*. For
reasons which elude me now, I did not copy the code back out of BBEdit.
During test, about the time I realized I needed to double-quote "\n", I
decided to slim it down with:

$, = " "; $\ = "\n";

.... making all the trailing newlines obsolete. You caught me; you win a
fully-operational Mac SE, loaded with important and useful software. You
must collect your prize in person taxes are the winner's responsibility
employees and family members are ineligible read fine print for contest
details.

In any case, the point of my post was all about shift and clobbering
actual params, not about my doofus print formatting.

Here's the actual code, in full, with output. Sorry, but you have to
change the sub call on each run. Uncomment if you want to raise Nixon
and Agnew from the grave; this *will* raise a fatal exception.

sub1: clobbers $foo only
sub2: clobbers neither
sub3: clobbers $bar only
sub4: clobbers neither
sub5: clobbers neither
sub6: clobbers both

* * *

use strict;

my $foo;
my $bar;
my $error;

$, = " "; $\ = "\n";

$foo = 'goodstuff';
$bar = 'morestuff';
$error = sub1($foo, $bar);
print $foo, $bar;

#$error = sub1('nixon', 'agnew');

sub sub1 {
$_[0] = 'bang';
print $_[0], $_[1];
return 0;
};
# bang morestuff
# bang morestuff

sub sub2 {
my $stuff = shift;
$stuff = 'bang';
print $_[0], $_[1];
return 0;
};
# morestuff
# goodstuff morestuff

sub sub3 {
my $stuff = shift;
$_[0] = 'bang';
print $_[0], $_[1];
return 0;
};
# bang
# goodstuff bang

sub sub4 {
my $stuff = shift;
$_[1] = 'bang';
print $_[0], $_[1];
return 0;
};
# morestuff bang
# goodstuff morestuff

sub sub5 {
@_ = ('bang', 'whimper');
print $_[0], $_[1];
return 0;
};
# bang whimper
# goodstuff morestuff

sub sub6 {
@_[0,1] = ('bang', 'whimper');
print $_[0], $_[1];
return 0;
};
# bang whimper
# bang whimper

* * *
 
X

Xiong Changnian

Dr.Ruud said:
Xiong Changnian schreef:


You should stop making such statements until you understand why it is
like it is.
But even if you don't, I won't notice it, goodbye.

Well, no need to be snooty. Enlighten me. I'm not even sure what part of
my questionable statement offends.

Are you questioning "call-by-reference"? I weaseled with "in essence". I
don't think it *is* a call-by-value if I *can* clobber it. On the other
hand, it's not a ref as Perl defines such things.

It seems to me that the call-by-ref nature of perl is something that can
be put to use but at times, it's exactly what's not wanted. So if actual
params get clobbered, you can blame Perl, acclaim Perl, or blame the
coder. I suppose I should have included the possibility of acclaiming
the coder for his brilliant decision. I can't for the life of me think
why you'd find this objectionable.

But, as I suggested, you're welcome to fill me in.

* * *

For other posters who are wondering why I dare to talk back to the
esteemed Dr. Rude, just let me say that I'm not going to apologize for
thinking or speaking -- today, tomorrow, or next week. If I'm wrong, I'm
wrong; I've been wrong before, I'll be wrong again. I'm always willing
to correct my mistakes but I'll never stop making them.

A strength of any community is the willingness and ability to welcome
newcomers on an equal footing.
 
X

Xiong Changnian

...it's harder to demonstrate than I thought.

To recap, I understand you to claim that actual params can be clobbered
within a called sub that shift-s.

sub x3 { shift() = 13 }

doesn't compile...

Well, here you're trying to assign to shift itself; I believe shift is
never an lvalue.
sub x1 { $_ = 13 for shift }

That does indeed clobber the actual param. As I understand it, your code
shifts off the first (or only) param -- by definition, not a list --
then treats it as a single-element list; the first (and only) trip
through the foreach loop, $_ is set to the first (and only) element of
that list. And $_ is an alias to the thing itself.

I guess if you try hard enough, you can break anything.
I had intended to delete
that sentence and dodge an explanation...

Why? This is what it's all about: finding the edges. Now, I'll be sure
not to do *that*. Thank you.
 
P

Peter J. Holzer

Well, no need to be snooty. Enlighten me. I'm not even sure what part of
my questionable statement offends.

Are you questioning "call-by-reference"? I weaseled with "in essence". I
don't think it *is* a call-by-value if I *can* clobber it. On the other
hand, it's not a ref as Perl defines such things.

It is call-by-reference. In other languages which pass parameters by
reference you don't have to explicitely dereference them either.
It seems to me that the call-by-ref nature of perl is something that can
be put to use but at times, it's exactly what's not wanted. So if actual
params get clobbered, you can blame Perl, acclaim Perl, or blame the
coder.

It wasn't clear who you were blaming of "sloppy coding". Larry Wall or
the coder of the subroutine which clobbers its arguments?

I think we can rule out that the fact that perl uses call-by-reference
is the result of sloppy coding. That was a design decision. We can
debate whether it was a good or a bad decision, but I think there is no
question that Larry thought about whether he wanted to implement
call-by-value or call-by-reference and decided for call-by-reference.

As for the coder of the routine: That may be sloppyness, or it may be
intentional.
I suppose I should have included the possibility of acclaiming
the coder for his brilliant decision.

Yes. That possibility does exist. If there would never be a reason for
clobbering an argument then implementing call-by-reference was a mistake
in the first place. You will notice that many of perls built-in
functions do clobber one of their arguments.

hp
 
X

Xiong Changnian

Peter J. Holzer said:
It wasn't clear who you were blaming of "sloppy coding"....

Sorry. I guess I took it for granted that any reader would understand
what I meant. I'm not sure I can be more lucid but I'll try.

When you deliberately pass refs to params, you must explicitly deref
them inside the sub. Therefore, there is a bit of confusion involved
when saying that Perl passes all params as refs. It's true; but a little
bit deeper in the machinery, the ref is dereffed for you. That's
probably an inexact statement but that's not the issue. As you say,
Peter, it *is* call-by-ref, even if the syntax says you're just passing
a scalar.

(This distinction is important to me because I avidly pass refs as
params when it's reasonable to do so; and because my code is seriously
OO and all objects are refs.)

Now, this can be a useful thing or it can cause problems -- like any
feature of any tool. You can choose to see it as a bug to be worked
around or a feature to be used; and if it bites you, you can blame
yourself -- or, if you want to be bullheaded, blame Perl. In my last
post, I covered the fourth possibility, in which you use the feature to
good effect and give yourself a pat on the back.

I hope I haven't stepped into some fanatic defense of Perl here. I
*like* it; I think it's better than competing P*'s; and I wouldn't
recommend Ruby due to immaturity -- **IMHO**! I assume that everyone
here thinks Perl is a Good Thing -- otherwise, why be here?

Is there some shibboleth I must pronounce to bypass this rigorous
loyalty-checking? I'm not really interested in fighting over the
Rightness of Perl -- on either side. I just want to know how it works. I
come here to ask questions and learn. I'm not trying to rewrite the
docs.
 
U

Uri Guttman

XC> In article <[email protected]>,

XC> When you deliberately pass refs to params, you must explicitly deref
XC> them inside the sub. Therefore, there is a bit of confusion involved
XC> when saying that Perl passes all params as refs. It's true; but a little
XC> bit deeper in the machinery, the ref is dereffed for you. That's
XC> probably an inexact statement but that's not the issue. As you say,
XC> Peter, it *is* call-by-ref, even if the syntax says you're just passing
XC> a scalar.

ok, let's try to clear up some stuff. first off all sub args are passed by
alias, not exactly by ref. it isn't deliberate or accidental, but all
the time. the callee has the aliases in @_ and can do with them what it
wants. the act of copying an alias is not dereferencing it but removing
the alias nature and making a copy of the original argument. it does not
break the alias which is still in @_. and i reiterate they are NOT refs
but aliases (similar to aliasing in foreach). you can't call ref on them
and get a ref type:

perl -le 'sub foo { print ref $_[0] } foo( 1 ) ;'

perl -le 'sub foo { print ref $_[0] } foo( [] ) ;'
ARRAY


XC> (This distinction is important to me because I avidly pass refs as
XC> params when it's reasonable to do so; and because my code is seriously
XC> OO and all objects are refs.)

refs are not aliases. you explicitly pass a ref since you want a real
ref to be used in the sub/method. you can't use @_ aliases as object or
other refs. so stop calling them refs.

XC> Now, this can be a useful thing or it can cause problems -- like any
XC> feature of any tool. You can choose to see it as a bug to be worked
XC> around or a feature to be used; and if it bites you, you can blame
XC> yourself -- or, if you want to be bullheaded, blame Perl. In my last
XC> post, I covered the fourth possibility, in which you use the feature to
XC> good effect and give yourself a pat on the back.

the only bug in in your understanding. @_ aliases are not refs. consider
this: when you assign (via indexing or shift) an alias from @_ to any
variable, it loses it alias nature. yet you can assign a ref to anything
else and it keeps its ref nature. @_ aliases are special. and one reason
they were made that way was for speed. it is much cheaper to push a list
of aliases onto a stack then to copy all of the arguments. and it allows
for the direct modification of the original arguments IF YOU WANT to do
that via aliases. if you don't you can always pass refs and copy those
as normal and modify away. YOUR CHOICE. in general it is always better
to pass refs yourself but there are some small cases where modifying via
aliases is a useful idea. not common but they exist.

XC> I hope I haven't stepped into some fanatic defense of Perl here. I
XC> *like* it; I think it's better than competing P*'s; and I wouldn't
XC> recommend Ruby due to immaturity -- **IMHO**! I assume that everyone
XC> here thinks Perl is a Good Thing -- otherwise, why be here?

you are not talking about the same thing so there is no fanatic
defense. you don't seem to understand aliases vs refs. that is the
problem, not perl.

XC> Is there some shibboleth I must pronounce to bypass this rigorous
XC> loyalty-checking? I'm not really interested in fighting over the
XC> Rightness of Perl -- on either side. I just want to know how it works. I
XC> come here to ask questions and learn. I'm not trying to rewrite the
XC> docs.

yes, you must come to an understanding of the actual topic first before
you flame away and babble incoherently. @_ aliases are not refs. you
think that because they refer to the original value that this is pass by
reference but it is not the same as most times you copy the args and
then they become pass by value. only a few subs should ever directly
modify their args via @_. and only a few more should directly access
@_. @_ is usually assigned to my vars or shifted (or both which can be
useful).

that is all.

uri
 
A

anno4000

Xiong Changnian said:
To recap, I understand you to claim that actual params can be clobbered
within a called sub that shift-s.

Quite so.
Well, here you're trying to assign to shift itself; I believe shift is
never an lvalue.

Pardon the pedantry, but I'm not trying to assign to "shift itself",
but to the return value of the function "shift" or, if you will, the
result of the list operator "shift". In view of the existence of lvalue
subs and other operators (substr for instance) that allow assignment to
their results that isn't entirely unreasonable. As I said before, the
restriction is syntactical and rather arbitrary.
That does indeed clobber the actual param. As I understand it, your code
shifts off the first (or only) param -- by definition, not a list --
then treats it as a single-element list; the first (and only) trip
through the foreach loop, $_ is set to the first (and only) element of
that list. And $_ is an alias to the thing itself.

I guess if you try hard enough, you can break anything.

One-shot for loops aren't that exotic in Perl. With for loops over
lists you get aliasing, which means that the loop variable ($_ in
this case) becomes another name for the list element. Assignment to
the loop variable changes the list element, unlike assignment to
a copy. Sometimes aliasing is all you want, there is only a single
element to loop over. Fortunately, the meaning of "for" (but not
the non-word "foreach") covers the one-shot case nicely.

That is what happens in "sub x1 { $_ = 13 for shift }". If you will,
it is nothing but a syntactically acceptable form of "shift() = 13".
It proves that what shift() returns *is* an lvalue and can be used to
clobber the actual parameter. What makes the parameter safe is
assignment to a (usually) lexical variable in the sub body. That
creates a copy of the value that has nothing to do with the original
parameter.
Why? This is what it's all about: finding the edges. Now, I'll be sure
not to do *that*. Thank you.

Perl's "postmodern", non-orthogonal design entails lots and lots of
edge cases where the actual behavior can't be derived from general
principles, but is geared to the particular case. It is often
distracting and sometimes impossible to discuss all that may be
relevant to a topic.

Normally, the use of shift() in a sub means the sub is not going
to change the parameter in question. If the form is the standard

my $var = shift;

you *know* that, because the only original is gone and all the sub
has is a copy[1]. If shift() is used in other ways, you must look
closer.

Anno

[1] If I were to discuss all ramifications I'd have to mention the
possibility that the same parameter could appear a second time
in the argument list and the sub could still change that.
 
A

anno4000

Uri Guttman said:
XC> In article <[email protected]>,

XC> When you deliberately pass refs to params, you must explicitly deref
XC> them inside the sub. Therefore, there is a bit of confusion involved
XC> when saying that Perl passes all params as refs. It's true; but a little
XC> bit deeper in the machinery, the ref is dereffed for you. That's
XC> probably an inexact statement but that's not the issue. As you say,
XC> Peter, it *is* call-by-ref, even if the syntax says you're just passing
XC> a scalar.

ok, let's try to clear up some stuff. first off all sub args are passed by
alias, not exactly by ref.

Here lies the terminological difficulty.

Traditionally, two (or more) styles of parameter passing are
distinguished: "by value", and "by reference", where "by reference"
allows the routine to change a value for the calling program and
"by value" does not. In this terminology, Perl's parameter passing
is clearly "by reference". Unfortunately, "reference" has a distinct
meaning in Perl terminology, and it is *not* the mechanism by which
Perl manages parameter passing, though conceivably it could be.
Instead, as you rightly say, Perl uses "aliasing" to realize its
parameter passing "by reference".

Terminology is that way.

[good stuff snipped]

Anno
 
X

Xiong Changnian

Uri Guttman said:
first off all sub args are passed by
alias, not exactly by ref.

Yes. I think we can hammer at this distinction all night long without
letting any more light into the room. I've tried to make it abundantly
clear that an alias is not, as Perl defines it, a ref(erence). However,
Perl does implement param passing via call-by-reference, generally
speaking. Otherwise, it would not be possible to do anything within a
sub that could clobber a param within the calling context.

Somebody at this point might say that an evil demon might pass a param
*value*, then look to see if $_[$i] had been changed and if so, go back
and put the new value back in the old mailbox. I don't know anything
about Perl internals but I'll bet a dollar to a doughnut that this is
not the case. Rather, I'll bet that $_[$i] points directly to the
mailbox itself. Please correct me if I'm wrong.

At this, my chosen level of abstraction, there are only two ways to pass
a param to a sub: call-by-reference or call-by-value. Perl does the
former. Am I mistaken? If so, how?
it isn't deliberate or accidental,
but all the time.

It is deliberate (on the part of the Perl maintainers) and it is all the
time. When somebody writing code in Perl fails to understand this
mechanism he may, by accident, screw up -- and if foolish, blame Perl,
if wise, study and learn. When somebody who understands it makes
deliberate use of it, he may thank Larry Wall, wisely, or compliment
himself, foolishly.

Have I finally and for all time covered these bases explicitly and
without said:
XC> (This distinction is important to me....)

.... and so it is. It is crystal clear in my mind -- both that params are
not automatically refs, as defined by Perl; and that @_ is a list of
parameters that are passed by the mechanism of call-by-reference. One
thing is not the other; I don't know how to make it any clearer. I've
done my best and I'm sorry if I've still come up short.

May the heavens guide the poor man who stumbles onto this thread,
thinking about passing a Perl ref as a param. It certainly can be done
(and often is) and this has nothing to do with the alias nature of @_.
@_ aliases are not refs. you
think that because they refer to the original
value that this is pass by reference but it is
not the same as most times you copy the args and
then they become pass by value.

If you copy the value, that doesn't make the calling mechanism
call-by-value. A language that implements parameter passing via
call-by-value will not (by default) pass anything to the sub that will
allow it to modify anything outside the sub (except, perhaps, global
variables and a return value, neither of which are enabled by the
parameter passing mechanism). That is:

use pseudocode;

sub foo (whang:integer) {
whang = 47;
};

main {

counter = 1;
foo (counter);
print counter;

};

.... will print 1, not 47. It *has* to print 1, because foo just does not
have any way at all to get *at* counter. When foo wakes up, it sees the
value 1 in whang, which it can use or ignore but cannot jimmy in any way
to reach "beyond" and grab hold of counter.

A language that implements pass-by-reference will print 47. That's all;
there is no in-between. An alias is a high-level abstraction that
encapsulates a reference -- not a Perl ref, not something that can be
interpreted as a Perl ref or explicitly dereffed. An alias is implicitly
dereffed when used.

Let me say again (in case it helps) that my primary orientation is way
down the ladder. When a sub invokes in most languages, it pops its
params off the stack. Now, at the moment of the pop, that word popping
off the stack is either the value that the calling routine had in "mind"
when deciding to call the sub; or that popped word is a pointer into the
heap or possibly another part of the stack, at which location the actual
value is found. The first case is call-by-value; the second
call-by-reference.
... you must come to an understanding
of the actual topic first before
you flame away and babble incoherently.

I believe I understand *this* point as clearly as possible; that leaves
a great deal of room for my education.

I've read others of this gentleman's posts and while it's not clear to
me whether he knows his stuff or not, he *seems* to me to lean rather
hard into other posters who don't appear to speak his language, his way.
Either way, I certainly don't want to insult him -- or anyone else. Yet
I would appreciate being allowed the same courtesy and I'm afraid I'm
not going to get it.

Am I right, in which case I should from now on ignore him? Or am I a
babbling fool? I leave it to the group to set me straight.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top