Private lvalue methods unusable?

I’m learning Ruby, and trying to grasp the non-declarative concept with
some small difficulties. Specifically, I can’t use a private lvalue
method the way I want. Please excuse me if this is a FAQ, but I
couldn’t find it.

Let’s say I have a class with a private method, say:

private
def dollars=(someDollars)
@cents = someDollars * 100
end

And if I want to use this method from some other method in the class, I
always create a local variable instead. I can’t prepend the method name
with a receiver (“self”) because the method is private.

For exmaple, this assigns to a local variable instead of using the
private method above:

public
def fillWallet
dollars = 100
puts “Wow! You’ve got $” + dollars().to_s
end

How do I call a private lvalue method?

Steve

···


Steven Smolinski => http://arbiter.ca/
GnuPG Public Key => http://arbiter.ca/steves_public_key.txt
=> or email me with ‘auto-key’ in the subject.
Key Fingerprint => 08C8 6481 3A7B 2A1C 7C26 A5FC 1A1B 66AB F637 495D

Hmmm… so self.dollars = 100 doesn’t work, eh? Only answer I can
think of is

self.send(:dollars=, 100)

Beautiful, isn’t it? :wink:

Actually, a better answer came to mind. Make :dollars= protected
instead of private. It works.

Gavin

···

On Friday, February 7, 2003, 10:35:08 AM, Steven wrote:

Let’s say I have a class with a private method, say:

private
def dollars=(someDollars)
@cents = someDollars * 100
end

And if I want to use this method from some other method in the class, I
always create a local variable instead. I can’t prepend the method name
with a receiver (“self”) because the method is private.

For exmaple, this assigns to a local variable instead of using the
private method above:

public
def fillWallet
dollars = 100
puts “Wow! You’ve got $” + dollars().to_s
end

How do I call a private lvalue method?

Hi,

···

In message “Private lvalue methods unusable?” on 03/02/07, Steven Smolinski steven.smolinski@sympatico.ca writes:

How do I call a private lvalue method?

No way, except using “send”, e.g.

self.send(:dollars=, 100)

						matz.

How do I call a private lvalue method?

Hmmm… so self.dollars = 100 doesn’t work, eh? Only answer I can
think of is

self.send(:dollars=, 100)

Beautiful, isn’t it? :wink:

It’s as ugly as sin, but it works as advertised. :slight_smile:

Actually, a better answer came to mind. Make :dollars= protected
instead of private. It works.

That works also, but it changes the spec. So does redefining the method
signature, like so: def dollars(aNumber). That also works, but changes
the spec.

I’ll just have to put this down as a “gotcha” and carry on. Is there a
list of places Ruby doesn’t follow the least surprise principle?

Thanks for the help,
Steve

···

Gavin Sinclair gsinclair@soyabean.com.au wrote:

On Friday, February 7, 2003, 10:35:08 AM, Steven wrote:

Steven Smolinski => http://arbiter.ca/
GnuPG Public Key => Webnewswire | Leading newswire service
=> or email me with ‘auto-key’ in the subject.
Key Fingerprint => 08C8 6481 3A7B 2A1C 7C26 A5FC 1A1B 66AB F637 495D

I’m assuming there’s some good reason for this that isn’t apparent to (at
least) me. Would someone care to enlighten me out of interest?

cheers
dim

···

In message “Private lvalue methods unusable?” > on 03/02/07, Steven Smolinski steven.smolinski@sympatico.ca writes:

How do I call a private lvalue method?

No way, except using “send”, e.g.

self.send(:dollars=, 100)

matz.

There’s a newcomers list that documents gotchas, but it doesn’t
include this one. I’ve never seen it mentioned before, actually.

You listening, Bill? :wink:

Gavin

···

On Friday, February 7, 2003, 1:55:40 PM, Steven wrote:

That works also, but it changes the spec. So does redefining the method
signature, like so: def dollars(aNumber). That also works, but changes
the spec.

I’ll just have to put this down as a “gotcha” and carry on. Is there a
list of places Ruby doesn’t follow the least surprise principle?

Hi,

···

In message “Re: Private lvalue methods unusable?” on 03/02/07, “Dmitri Colebatch” dim@colebatch.com writes:

self.send(:dollars=, 100)

I’m assuming there’s some good reason for this that isn’t apparent to (at
least) me. Would someone care to enlighten me out of interest?

“private” does mean “can only be called without explicit receiver”.
This is the reason.

						matz.

Private methods can’t have an explicit receiver (i.e., you can’t do
self.method on a private). Calling it directly looks like this:

dollars = 100

So a private lvalue method and assignment to a local variable have the
exact same syntax. Which means ruby had to choose whether it should:

  1. look for a method and raise an exception if there is none, or
  2. create a local variable.

It appears ruby does (2).

I suppose there’s always (3), which is to look for a method first and
create a local variable if there’s no method defined. But that’s some
serious semantic gymnastics, and may make things worse rather than
better. I don’t know enough about ruby to comment on that.

Steve

···

Dmitri Colebatch dim@colebatch.com wrote:

In message “Private lvalue methods unusable?” >> on 03/02/07, Steven Smolinski steven.smolinski@sympatico.ca writes:

How do I call a private lvalue method?

No way, except using “send”, e.g.

self.send(:dollars=, 100)

I’m assuming there’s some good reason for this that isn’t apparent to
(at least) me. Would someone care to enlighten me out of interest?


Steven Smolinski => http://arbiter.ca/
GnuPG Public Key => Webnewswire | Leading newswire service
=> or email me with ‘auto-key’ in the subject.
Key Fingerprint => 08C8 6481 3A7B 2A1C 7C26 A5FC 1A1B 66AB F637 495D

Hi –

···

On Fri, 7 Feb 2003, Steven Smolinski wrote:

I’ll just have to put this down as a “gotcha” and carry on. Is there a
list of places Ruby doesn’t follow the least surprise principle?

Only Matz can tell you, because POLS in Ruby is specifically defined
as POLS for Matz :slight_smile:

David


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Hi Gavin,

Are you sure this belongs in the newcomers list? I never use
‘private’ either :slight_smile: (I just put all the “private” methods in the C part.)

Regards,

Bill

···

Gavin Sinclair gsinclair@soyabean.com.au wrote:

There’s a newcomers list that documents gotchas, but it doesn’t
include this one. I’ve never seen it mentioned before, actually.

You listening, Bill? :wink:

Gavin

Hi,

self.send(:dollars=, 100)

I’m assuming there’s some good reason for this that isn’t apparent to (at
least) me. Would someone care to enlighten me out of interest?

“private” does mean “can only be called without explicit receiver”.
This is the reason.

It it a bad idea to relax the restriction, “can only be called
without explicit receiver or with `self’ receiver”?

class C
private
def x=(val)
@x = val
end
public
def set_x(val)
self.x = val # OK
self
end
end

C.new.set_x(1) # OK
C.new.x = 1 # NG

···

At Fri, 7 Feb 2003 13:29:58 +0900, Yukihiro Matsumoto wrote:


Nobu Nakada

Not sure, no, since this is the first time it has come up. It is
surprising behaviour though.

After all, dollar= is supposed to be “just another method”, isn’t it?

Gavin

···

On Friday, February 7, 2003, 3:56:14 PM, William wrote:

Gavin Sinclair gsinclair@soyabean.com.au wrote:

There’s a newcomers list that documents gotchas, but it doesn’t
include this one. I’ve never seen it mentioned before, actually.

You listening, Bill? :wink:

Gavin

Hi Gavin,

Are you sure this belongs in the newcomers list? I never use
‘private’ either :slight_smile: (I just put all the “private” methods in the C part.)

Hi,

···

In message “Re: Private lvalue methods unusable?” on 03/02/07, nobu.nokada@softhome.net nobu.nokada@softhome.net writes:

“private” does mean “can only be called without explicit receiver”.
This is the reason.

It it a bad idea to relax the restriction, “can only be called
without explicit receiver or with `self’ receiver”?

Hmm, interesting. Let me consider this issue.

						matz.

Hi,

“private” does mean “can only be called without explicit receiver”.
This is the reason.

It it a bad idea to relax the restriction, “can only be called
without explicit receiver or with `self’ receiver”?

Hmm, interesting. Let me consider this issue.

Forgot an example. Error if the receiver isn’t literally
`self’, even if it actually equals self.

private :x=
def my
  self
end
def my_x(val)
  my.x = val	# NG
end

And patch.

Index: eval.c

···

At Fri, 7 Feb 2003 18:00:35 +0900, Yukihiro Matsumoto wrote:

RCS file: /cvs/ruby/src/ruby/eval.c,v
retrieving revision 1.392
diff -u -2 -p -r1.392 eval.c
— eval.c 7 Feb 2003 06:30:18 -0000 1.392
+++ eval.c 7 Feb 2003 08:14:53 -0000
@@ -1924,6 +1924,7 @@ is_defined(self, node, buf)
goto check_bound;

  •  case NODE_CALL:
     case NODE_ATTRASGN:
    
  • if (node->nd_recv == (NODE *)1) goto check_bound;
  •  case NODE_CALL:
    
    PUSH_TAG(PROT_NONE);
    if ((state = EXEC_TAG()) == 0) {
    @@ -2746,13 +2747,21 @@ rb_eval(self, n)
    VALUE recv;
    int argc; VALUE argv; / used in SETUP_ARGS */
  •   int scope;
      TMP_PROTECT;
    
      BEGIN_CALLARGS;
    
  •   recv = rb_eval(self, node->nd_recv);
    
  •   if (node->nd_recv == (NODE *)1) {
    
  •   recv = self;
    
  •   scope = 1;
    
  •   }
    
  •   else {
    
  •   recv = rb_eval(self, node->nd_recv);
    
  •   scope = 0;
    
  •   }
      SETUP_ARGS(node->nd_args);
      END_CALLARGS;
    
      SET_CURRENT_SOURCE();
    
  •   rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,0);
    
  •   rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,scope);
      result = argv[argc-1];
    
    }
    @@ -4092,10 +4101,18 @@ assign(self, lhs, val, pcall)
    {
    VALUE recv;
  •   recv = rb_eval(self, lhs->nd_recv);
    
  •   int scope;
    
  •   if (lhs->nd_recv == (NODE *)1) {
    
  •   recv = self;
    
  •   scope = 1;
    
  •   }
    
  •   else {
    
  •   recv = rb_eval(self, lhs->nd_recv);
    
  •   scope = 0;
    
  •   }
      if (!lhs->nd_args) {
      /* attr set */
      ruby_current_node = lhs;
      SET_CURRENT_SOURCE();
    
  •   rb_call(CLASS_OF(recv), recv, lhs->nd_mid, 1, &val, 0);
    
  •   rb_call(CLASS_OF(recv), recv, lhs->nd_mid, 1, &val, scope);
      }
      else {
    

@@ -4108,5 +4125,5 @@ assign(self, lhs, val, pcall)
SET_CURRENT_SOURCE();
rb_call(CLASS_OF(recv), recv, lhs->nd_mid,

  •   	RARRAY(args)->len, RARRAY(args)->ptr, 0);
    
  •   	RARRAY(args)->len, RARRAY(args)->ptr, scope);
      }
    
    }
    Index: parse.y
    ===================================================================
    RCS file: /cvs/ruby/src/ruby/parse.y,v
    retrieving revision 1.254
    diff -u -2 -p -r1.254 parse.y
    — parse.y 5 Feb 2003 08:11:27 -0000 1.254
    +++ parse.y 7 Feb 2003 08:17:19 -0000
    @@ -1403,5 +1403,8 @@ primary : literal
    > primary_value ‘[’ aref_args ‘]’
    {
  •   	$$ = NEW_CALL($1, tAREF, $3);
    
  •   	if (nd_type($1) == NODE_SELF)
    
  •   	    $$ = NEW_FCALL(tAREF, $3);
    
  •   	else
    
  •   	    $$ = NEW_CALL($1, tAREF, $3);
          }
      > tLBRACK aref_args ']'
    

@@ -4628,7 +4631,13 @@ call_op(recv, id, narg, arg1)
if (narg == 1) {
value_expr(arg1);

  • arg1 = NEW_LIST(arg1);
  • }
  • else {
  • arg1 = 0;
    }
  • return NEW_CALL(recv, id, narg==1?NEW_LIST(arg1):0);
  • if (recv && nd_type(recv) == NODE_SELF)
  • return NEW_FCALL(id, arg1);
  • return NEW_CALL(recv, id, arg1);
    }

@@ -4668,4 +4677,6 @@ match_gen(node1, node2)
}

  • if (node1 && nd_type(node1) == NODE_SELF)
  • return NEW_FCALL(tMATCH, NEW_LIST(node2));
    return NEW_CALL(node1, tMATCH, NEW_LIST(node2));
    }
    @@ -4784,5 +4795,8 @@ aryset(recv, idx)
    NODE *recv, *idx;
    {
  • value_expr(recv);
  • if (recv && nd_type(recv) == NODE_SELF)
  • recv = (NODE *)1;
  • else
  • value_expr(recv);
    return NEW_ATTRASGN(recv, tASET, idx);
    }
    @@ -4802,5 +4816,8 @@ attrset(recv, id)
    ID id;
    {
  • value_expr(recv);
  • if (recv && nd_type(recv) == NODE_SELF)
  • recv = (NODE *)1;
  • else
  • value_expr(recv);
    return NEW_ATTRASGN(recv, rb_id_attrset(id), 0);
    }
    @@ -5318,4 +5335,5 @@ new_call(r,m,a)
    NODE *a;
    {
  • if (r && nd_type(r) == NODE_SELF) return new_fcall(m, a);
    if (a && nd_type(a) == NODE_BLOCK_PASS) {
    a->nd_iter = NEW_CALL(r,m,a->nd_head);


Nobu Nakada