Subclassing Struct.new

M

Minkoo Seo

Hi group.

Please have a look at the following:

irb(main):001:0> class F < Struct.new :n
irb(main):002:1> def dec_n
irb(main):003:2> n = n - 1
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> f = F.new 1
=> #<struct F n=1>
irb(main):007:0> f.dec_n
NoMethodError: undefined method `-' for nil:NilClass
from (irb):3:in `dec_n'
from (irb):7
from :0
irb(main):008:0>

I've tried to define a class which decrease n by 1, but failed. How can
I do that?

- Minkoo Seo
 
M

Minkoo Seo

It works! Thank you.

But, could you tell me why should I have to use self?

- Minkoo Seo
 
C

chiaro scuro

It works! Thank you.

But, could you tell me why should I have to use self?

The alternative would be to use @n, but I am not sure that Struct
organises these properties around individual instance variable. it
probably keeps them in an internal hash, so you have to use the
accessor 'n'. However, when you use it on the left handside you must
prefix with self, otherwise ruby thinks it is a variable n, rather
than a call to the attribute writer 'n=3D'.

I am not sure if this is supposed to be a bug.. it's certainly a
surprising behaviour.
 
M

Minkoo Seo

chiaro said:
However, when you use it on the left handside you must
prefix with self, otherwise ruby thinks it is a variable n, rather
than a call to the attribute writer 'n='.

It is strange because

n=1 is fine, but
n -= 1 is not.
I am not sure if this is supposed to be a bug.. it's certainly a
surprising behaviour.

Counter intuitive, at the very least. I really want this behavior to be
fixed.

Best,
Minkoo Seo
 
R

Ross Bamford

It is strange because

n=1 is fine, but
n -= 1 is not.

Well, n = 1 just assigns the fixnum 1 to a (new) local variable 'n'.
n -= 1 is expanded to n = n + 1.

Normally, n + 1 would end up calling your method because Ruby would have
to figure out whether it's a method or variable, but because in this
case Ruby has seen a bare assignment to 'n' by that point, it remembers
that and assumes 'n' is a local variable.

This local variable isn't yet initialized (the n + 1 would be it's
initializer), so n + 1 ends up being nil + 1, or (effectively) nil.+(1),
hence the "undefined method '+' for nil:NilClass".

Using self.n = 1 forces Ruby to treat the assignment as involving the
method 'n' on 'self'.

I don't think it's a bug, but I know it's tripped people (including me)
up before. From the implementation point of view it's probably the
lesser of two evils, though.
 
M

Minkoo Seo

Ross said:
Normally, n + 1 would end up calling your method because Ruby would have
to figure out whether it's a method or variable, but because in this
case Ruby has seen a bare assignment to 'n' by that point, it remembers
that and assumes 'n' is a local variable.

This local variable isn't yet initialized (the n + 1 would be it's
initializer), so n + 1 ends up being nil + 1, or (effectively) nil.+(1),
hence the "undefined method '+' for nil:NilClass".

Sigh. This reminds me of the rule that I have to use
"this->method_name( )" when I want call a method of T, parent class,
which is passed as template parameter. Anyway, it's not a perfect
world...

Best,
Minkoo Seo
 
C

chiaro scuro

This local variable isn't yet initialized (the n + 1 would be it's
initializer), so n + 1 ends up being nil + 1, or (effectively) nil.+(1),
hence the "undefined method '+' for nil:NilClass".

Using self.n =3D 1 forces Ruby to treat the assignment as involving the
method 'n' on 'self'.

I don't think it's a bug, but I know it's tripped people (including me)
up before. From the implementation point of view it's probably the
lesser of two evils, though.

It's really unrubesque and tricky. What would be the consequences of
handling it as you would normally expect? What would the side effects
be?
 
R

Ross Bamford

It's really unrubesque and tricky. What would be the consequences of
handling it as you would normally expect? What would the side effects
be?

This isn't necessarily based in fact (i.e. I've not surfed the source
just now) but I imagine it would be:

* Harder to parse, with more ambiguous cases.
* Generally slower, since local lookup would be 'the long way'
more often.
* More difficult to follow the code six months down the line
(or on the next guy's screen).
 
C

chiaro scuro

This isn't necessarily based in fact (i.e. I've not surfed the source
just now) but I imagine it would be:

* Harder to parse, with more ambiguous cases.
* Generally slower, since local lookup would be 'the long way'
more often.
* More difficult to follow the code six months down the line
(or on the next guy's screen).

I would disagree on the third point.

However, I guess that doing allowing 'selfless' attributes would cause
some worst inconsistency somewhere else. i just wonder what it could
be.
 
M

Markus Werner

Hello,


Minkoo said:
Counter intuitive, at the very least. I really want this behavior to be
fixed.

How should it be in your opinion?

I find it intuitive, cause the sense behind it is obviously.

Ruby assumes that a method call has the current object as receiver.
So it implied the "self." prefix"
But if there is a local var assignment it assumes that you like to use this
local var instead.
Since the local var is determined by the enclosing block it is IMHO an
intuitive solution.

What do you ruby expect to do if it see some think like this?

def foo
a = "hello"
end

Should it assign the local var "a" or should it call the method "a="?
If you say that it should assign the local var "a" just if there is no
method "a=".

What happend to your code if some day in the future you or some else define
a methode "a=" to write the ivar @a. And you don't remember that your
method foo use a local var a?

with kind regards


Markus

PS
What should happend in this case?

def n
@n
end

def n=(n)
@n = n
end
 
C

chiaro scuro

Should it assign the local var "a" or should it call the method "a=3D"?
If you say that it should assign the local var "a" just if there is no
method "a=3D".

thanks for pointing out the side effects of the other stylistic
choice. now it's much clearer why it was done this way.
What happend to your code if some day in the future you or some else defi= ne
a methode "a=3D" to write the ivar @a. And you don't remember that your
method foo use a local var a?

my tests would fail, I guess.

I also program in VBA, where you have a 'property set' feature that is
roughly the equivalent of the 'name=3D' operator in Ruby and it has
never been a problem. the few times this error slipped through, the
tests always caught it.
What should happend in this case?

def n
@n
end

def n=3D(n)
@n =3D n
end

I guess the 'n' in '@n =3D n' would be the one most internally scoped,
i.e. the passed arg
 
M

Mark Wilden

However, I guess that doing allowing 'selfless' attributes would cause
some worst inconsistency somewhere else. i just wonder what it could
be.

One explanation I've heard for this behavior is that if 'self' was implied
in this case, but you actually -did- want to use a local variable with the
same name, that some kind of prefix would be required (which would be
weirder than the current behavior).
 
H

Hal Fulton

Minkoo said:
It is strange because

n=1 is fine, but
n -= 1 is not.




Counter intuitive, at the very least. I really want this behavior to be
fixed.

Please explain what you mean.

Hal
 
M

Markus Werner

Hi chiaro,

I'm not a regular programmer. So I guess I don't have a chance to discuss
with you and the right level. ;-)

For me the subject we discuss it is a matter of taste. And for my taste the
ruby way is OK.

I like it the way it is and for me it is not surprising and thats why I
wrote my post.
my tests would fail, I guess.

Yes this should happen.

I also program in VBA, where you have a 'property set' feature that is
roughly the equivalent of the 'name=' operator in Ruby and it has
never been a problem. the few times this error slipped through, the
tests always caught it.

Wouldn't it be better if they don't appear at the first place?

Maybe you could explain me why the other way is better.
I'm open minded so your opinion would really helps do to extend my
knowledge.


with kind regards

Markus
 
C

chiaro scuro

Hi Markus, sorry if it took me a while to answer back. It was a busy week.

Hi chiaro,
For me the subject we discuss it is a matter of taste. And for my taste t= he
ruby way is OK.
I like it the way it is and for me it is not surprising and thats why I
wrote my post.

The reason I find it surprising is that I expect to read from
something and to write to something using the same name to identify
the thing I am reading from or writing to.

I find this symmetry beautiful and intuitive.

If 'puts n' prints out the value of n, then I expect 'n =3D something'
to change the value of n. I don't want to be concerned with the fact
that n is a local variable or a method that provides me with access to
an instance variable.

With the current system I would have to specify 'self.n =3D something'
to make the assignment. This is unrebesque to me, since it forces me
to see (and to understand) the inner workings of the system, rather
than focus on my problem.

Yet, the discussion on this thread pointed out why it is necessary (see lat=
er)
Wouldn't it be better if they don't appear at the first place?

This is a matter of attitude. I used to be all for 'defensive
programming' [1], but I find that tests can allow me to move faster
while catching errors fast.

I prefer to write code where the semantics of what I am doing are
clear to me, rather than having to satisfy the needs of the parser.=20
This means that I can be more relaxed and I do not need to specify the
syntactic nature of what I am accessing (local var, instance
attribute, etc..) unless it is necessary to solve an ambiguous
situation.

Maybe you could explain me why the other way is better.
I'm open minded so your opinion would really helps do to extend my
knowledge.

I hope I made my point :)

The reason why we cannot get rid of the self, as far as I have
understood it, it is related to the use of mixins. If we didn't have
mixins I would simply propose to resolve the nature of our 'n' sign at
runtime. If there is an argument 'n', then that argument is 'n'
within our scope, and we would need an explicit 'self.n' to access our
'n' method/accessor. If there is no 'n' argument, then we look for
'n' methods and finally for 'n' local variables.

If you use mixins however you can have an unpredictable name clash
between names that were intended to be used as local variables within
the mixin and names that you are using as methods in your class. This
is an error that is very hard to spot, since you are not even supposed
to know what these offending local variable names of the mixin are.=20
They are inner workings of the mixin; they are not part of the mixin
'interface' and they should not conflict with your class.

This looks like a problem that is almost impossible to solve in a
non-defensive way.. forcing us to use self in the former case as well,
to protect us from an eventual mixin name clash ambiguity.

I just wish there could be a way to solve the mixin name clashes and
get rid of the 'self' when using the assignment operator on a method.


[1] : http://en.wikipedia.org/wiki/Defensive_programming
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top