[ruby-talk:02319] Re: Question about attribute writers

From: mrilu <mrilu@...>
Date: 2000-04-01 00:45:11 UTC
List: ruby-talk #2319

On Fri, 31 Mar 2000, Clemens Hintze wrote:

> Dave Thomas writes:
>  class Fred
>    def age
>      @age
>    end
>    def age=(val)
>      @age = val
>    end
>    def ageInMonths
>      age * 12
>    end
>    def ageInMonths=(val)
>      age = val / 12
>    end
>    def initialize(age)
>      @age = age
>    end
>  end
>
>  f = Fred.new(10)
>  p f.age
>  p f.ageInMonths
>  f.ageInMonths = 300
>  p f.age
>
>I get
>
>  10
>  120
>  10

> > The reason is that the assignment
> >
> >    age = val/12
> >
> > in Fred#ageInMonths= is creating a local variable 'age', rather than
> > invoking the age= method.
> > This seems inconsistent with the fact that
> >
> >   age / 12
> >
> > in Fred#ageInMonths is correctly using the attribute reading
> > function.

> > Is this the expected behavior?

I can't expect it even if I somebody says there's a trick in here.
So following will try to give some light what I expected.

Clemens:
> At least I would expect it! It is like answered in your FAQ! How does
> Ruby determine what a variable is? It is really stright-forward, IMHO.
>
>    Look if there was an assignment before
>       yes => compile a variable access
>       no  => compile a method call.
>
> That means, the compiler seems to prefer to compile variable access
> over compiling method calls. So
> 
>    age / 12
> 
> would let the interpreter look whether if has already seen a
> assignment to a local variable. As it didn't it compile a method call!
> So Fred#age would be called. However

Agreed.

>    age = val / 12
> 
> looks like an assignment. That means variable access! As Ruby prefer
> this, why should the compiler try to compile method call here?

Disagreed. If I look your excerpt from FAQ I see "if there was". And
before this line I haven't seen assignment to age. On this line
interpreter sees assignment, but that doesn't qualify your requirement of
"was assigned already".

But the requirement is fuzzy itself at least in these ways
- assignment to local variable / assignment to accessors
- "before" at compile time (at parsing) / at runtime (hopefully not)

So if we think that requirement is (I reformulate it a little):
"Code is interpreted as variable access if at compile time we have already
seen assignment to local variable in current scope".

I think we face a problem here. With this requirement we can't ever
assign local variable, thus create one.

So I think we have to loose up a little. 

"When we have to test whether code is interpreted as a instance call or
access to a local variable, local variable wins: 
1) at write access (assignment) always 
   (to be able to create local variables)
2) at read access if we have seen 1) earlier in local scope. "

Now the problem is that we can't ever call age=()-method inside class
declaration.

val = age * 2  # 2) condition not met, so calling age()
age = val / 2  # 1) no condition, so create new local variable
val = age * 2  # 2) condition met at previous row, 
               #    so accessing local variable
age = val / 2  # 1) no condition, so accessing local variable

> But, however, if it was written as
> 
>    self.age = val / 12

There was a catch in the last paragraph. We can't call age=() directly,
but with self I think we should be able to say we really mean self.age()
at read access and self.age=() at write access.

> then Ruby is not able to think of an variable access here, as this
> syntax is invalid at that point! So it will compile a method call to
> Fred#age=

I'd like to clarify this a little. I think self.age can't be tried to
understand as a local variable access because it is no local variable. Not
because it is not valid syntax (it is I guess, there is just no syntax
to interpret it as local variable, but there is syntax to interpret it
as self's method).

One additional question: why we need age= at all in Dave's example if we
have declared age as it is.

I would expect

age() = val / 12  --> @age = val / 12

Then it's up to definition whether age = val / 12 means age() = val / 12.

> Sorry for my confusing explanation. But I hope you got my point
> anyway.

When I read your mail I was full of understanding. Now that I wrote this
everything fades away... :)

In This Thread