Simple and stuppid bug (can anyone find it?)

P

paul

Hi all,
I thought it to be good programming style to make attribute assignment
chainable. When using the attr_writer an assignment returns te value
assigned which is not what I want, so I tried to do-it-myself.
The result is below. This doesn't work. When running Ruby gives:
undefined method 'b=' for "AA":String (NoMethodError)
Anybody, please tell me: *why*. What simple stuppid thing am I
overseeing?
Thanks in advance!
Cheers,
Paul

--the code--
class A
def initialize
end
def a
return @a
end
def a=(someA)
@a = someA
return self
end
def b
return @b
end
def b=(someB)
@b = someB
return self
end
end

puts A.new.a=("AA").b=("BB").a
--the code--
 
X

Xavier Noria

Hi all,
I thought it to be good programming style to make attribute assignment
chainable. When using the attr_writer an assignment returns te value
assigned which is not what I want, so I tried to do-it-myself.
The result is below. This doesn't work. When running Ruby gives:
undefined method 'b=' for "AA":String (NoMethodError)
Anybody, please tell me: *why*. What simple stuppid thing am I
overseeing?

That is explained on page 85 of the Pickaxe (113 in my PDF edition):

"In older Ruby versions, the result of the assignment was the value
returned by the attribute-setting method. In Ruby 1.8, the value of
the assignment is always the value of the parameter; the return
value of the method is discarded."

-- fxn
 
D

David Vallner

--------------enigC2A67252DAFBD93047DFE80A
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Hi all,
I thought it to be good programming style to make attribute assignment
chainable. When using the attr_writer an assignment returns te value
assigned which is not what I want, so I tried to do-it-myself.
The result is below. This doesn't work. When running Ruby gives:
undefined method 'b=3D' for "AA":String (NoMethodError)
Anybody, please tell me: *why*. What simple stuppid thing am I
overseeing?
Thanks in advance!
Cheers,
Paul
=20
--the code--
class A
def initialize
end
def a
return @a
end
def a=3D(someA)
@a =3D someA
return self
end
def b
return @b
end
def b=3D(someB)
@b =3D someB
return self
end
end
=20
puts A.new.a=3D("AA").b=3D("BB").a
--the code--
=20
=20

There is no bug in your code, apparently Ruby always returns the rvalue
from what parses as an assignment.

With your class loaded into irb, I got:

irb(main):039:0> A.new.a=3D("AA")
=3D> "AA"

irb(main):043:0> A.new.send('a=3D', 'AA') # Does what you expected.
=3D> #<A:0x3b3015c @a=3D"AA">


So, can anyone recall a rationale for the behaviour (consistency with
mainstream programming languages with regards to assignment?) or is this
a Ruby bug?

Either way, disclaimer: I don't like what you're trying to do in the
first place. Pretty much anyone reading your code will expect assignment
to return the rvalue because that's what #attr_writer does, and the
chained assignment in your example just reads plain ugly to me,
especially since I subconsciously parse it as:
A.new.a =3D "AA".b =3D "BB".a
i.e. ``assign "BB".a to -both- "AA".b and A.new.a''
and therefore am in favour of consistency with assignment in other
languages instead of consistency with Javaesque property setters (where
the ambiguity doesn't manifest.)

David Vallner


--------------enigC2A67252DAFBD93047DFE80A
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFcqysy6MhrS8astoRAgD1AJ9oERqfAqkIwBT9VJYSnnSfluQFXwCfUBKb
24fEAPvL1iiULSF9paVZyys=
=i/Ea
-----END PGP SIGNATURE-----

--------------enigC2A67252DAFBD93047DFE80A--
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: Simple and stuppid bug (can anyone find it?)"

|So, can anyone recall a rationale for the behaviour (consistency with
|mainstream programming languages with regards to assignment?) or is this
|a Ruby bug?

It's intended behavior. Attribute assignment is assignment (even if
it's implemented by method call). And that's how assignment work for
many languages, including Ruby.

matz.
 
M

Martin DeMello

Hi all,
I thought it to be good programming style to make attribute assignment
chainable. When using the attr_writer an assignment returns te value
assigned which is not what I want, so I tried to do-it-myself.

If you do want to do this, I'd suggest an attr_setter method, that
generates both foo= and set_foo methods. Then you can chain via

a.set_a(100).set_b(10)

which looks much better than the a.a=(100).b=(10)

martin
 
P

paul

:) thanks all for the educating responses. Martin, I really liked you
suggestion.

Let me explain the reason why I did want to chain assignments:
I have a class that has a lot of attributes that can be written at
creation of an instance. To save my typing the whole lot I made a
constructor getting all those attributes. Though, I expect a lot more
attributes in the near future. Since the attributes are not mandatory
for creating the instance using the constructor-method seemed a little
rigid.
My second idea was to use attr_writer to set all the properties, but
chaining seemed saving me some lines (these assignments aren't that
exciting after all).

Is there any moral/principal reason to write:
A.a = "AA"
A.b = "BB"
instead of:
A.set_b("BB").set_a("AA")

?

Cheers,
Paul


Martin DeMello schreef:
 
D

dblack

Hi --

:) thanks all for the educating responses. Martin, I really liked you
suggestion.

Let me explain the reason why I did want to chain assignments:
I have a class that has a lot of attributes that can be written at
creation of an instance. To save my typing the whole lot I made a
constructor getting all those attributes. Though, I expect a lot more
attributes in the near future. Since the attributes are not mandatory
for creating the instance using the constructor-method seemed a little
rigid.
My second idea was to use attr_writer to set all the properties, but
chaining seemed saving me some lines (these assignments aren't that
exciting after all).

Is there any moral/principal reason to write:
A.a = "AA"
A.b = "BB"
instead of:
A.set_b("BB").set_a("AA")

I guess my question would be: why invent a new way to assign to
instance variables, when Ruby already has a nice way to do that?
Also, I tend to read set_b("BB") as returning "BB". And understanding
it, at the very least, requires knowing about your set_attr method,
which adds another unnecessary layer.

It's not that I think, "Wow, that's elegant!" every time I see an
assignment. They sort of fade into the background. But a new way of
doing them, unless it's really adding value, does draw attention to
itself, largely of a negative kind I would expect.

(I agree with Martin that that's about the best you're going to do if
you feel you have to chain these things, but I'd counsel against the
chaining.)


David

--
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
 
M

Martin DeMello

:) thanks all for the educating responses. Martin, I really liked you
suggestion.


Is there any moral/principal reason to write:
A.a = "AA"
A.b = "BB"
instead of:
A.set_b("BB").set_a("AA")

Actually, when I have this problem, I tend to use a third option:

class A
def initialize(args)
set args
end

def set(arg_hash)
arg_hash.each_pair {|k, v|
self.send:)"#{k}=", v)
}
self
end
end

Now you can do a = A.new:)foo => "hello", :bar => "world").whatever
(assuming, of course, that you have appropriately called attr_writer
:foo, :bar or otherwise defined #foo= and #bar= first).

If you only want to set the instance variables when you create the
object, you can get rid of set and inline the code into initialize
instead.

martin
 
D

David Vallner

--------------enig2A75EE277CB3DBA96007224F
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
My second idea was to use attr_writer to set all the properties, but
chaining seemed saving me some lines (these assignments aren't that
exciting after all).
=20

Either way, I don't agree with the motivation, it's very easy to lose
track of what you've assigned to in a method chain as an extension of
the fact any method chain is a detriment to readability if the steps
aren't trivial (e.g. require keeping in your head what the previous step
returned).

Personally, I'd go with the options hash Martin DeMello proposed. It's
rather idiomatic, easier to read (a bit) than a method chain since
initialisation logic is kept in the call to #new, and doesn't involve
superfluous typing.

David Vallner


--------------enig2A75EE277CB3DBA96007224F
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFcvjry6MhrS8astoRAoJxAJsG2SSzFY5TSW2s5V+C4myJbXSFGACfaLdI
qTkhSWoRvkrgUcS6Ap9oqLs=
=5zwv
-----END PGP SIGNATURE-----

--------------enig2A75EE277CB3DBA96007224F--
 
M

Mike Durham

David said:
Either way, I don't agree with the motivation, it's very easy to lose
track of what you've assigned to in a method chain as an extension of
the fact any method chain is a detriment to readability if the steps
aren't trivial (e.g. require keeping in your head what the previous step
returned).

Personally, I'd go with the options hash Martin DeMello proposed. It's
rather idiomatic, easier to read (a bit) than a method chain since
initialisation logic is kept in the call to #new, and doesn't involve
superfluous typing.

David Vallner
In my opinion, if I understood the problem correctly, if a
function/method ends with a 'return self (or whatever)' then that's what
should be returned. There is no acceptable excuse for nor returning it.
Cheers, Mike
 
P

paul

:) you would expect it yes :) (I did)
Without the help of the guy's here I would have probably taken a while
to figure it out myself.
It's clear for me now. The new rule, written into my brain: methodes
whose names end in '=' always return the first argument. Writing this
down made me think: with multiple arguments, does it return them all,
or just the first. I gave it a try, and it seems like I'm not allowed
to give more then one argument to an methodes whose names end in '='.

Can anyone confirm this as a rule?

irb(main):001:0> class A
irb(main):002:1> def a=(b)
irb(main):003:2> puts 'in a=(b)'
irb(main):004:2> @a = b
irb(main):005:2> return 'c'
irb(main):006:2> end
irb(main):007:1> def a
irb(main):008:2> return a
irb(main):009:2> end
irb(main):010:1> end
=> nil
irb(main):011:0> aa = A.new
=> #<A:0x2c1067c>
irb(main):012:0> aa.a = 'b'
in a=(b)
=> "b"
irb(main):013:0> class X
irb(main):014:1> def x=(y, z)
irb(main):015:2> puts 'in x=(y, z)'
irb(main):016:2> @x = [y, z]
irb(main):017:2> return 'w'
irb(main):018:2> end
irb(main):019:1> def x
irb(main):020:2> return @x
irb(main):021:2> end
irb(main):022:1> end
=> nil
irb(main):023:0> xx = X.new
=> #<X:0x2bfd194>
irb(main):024:0> xx.x=('y', 'z')
SyntaxError: compile error
(irb):24: syntax error
xx.x=('y', 'z')
^
from (irb):24
irb(main):025:0>
 
D

David Vallner

--------------enig91A1DD531F2507F32A81B5DE
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
The new rule, written into my brain: methodes
whose names end in '=3D'=20

Read: assignments.
always return the first argument.

As assignments do.

Don't think about attribute writes as methods. Don't write them as
methods, it just looks ugly and confuses people, apparently including
you ;;P
Writing this
down made me think: with multiple arguments, does it return them all,
or just the first. I gave it a try, and it seems like I'm not allowed
to give more then one argument to an methodes whose names end in '=3D'.=

That's because you're not calling the method per se. You're doing an
attribute write. The Ruby parser reads it as an assignment. The rvalue
isn't even being suspected of being a parenthetised argument list.

In your code, xx.x=3D('y', 'z') probably gets parsed as:
xx.x =3D ('y', 'z'),
where the assignment rvalue ('y', 'z') is invalid Ruby.

You're not allowed to have attribute setters take two parameters,
because it's a syntax feature to set a value to an attribute.

Just as a variable always points to -one- object, an attribute points to
one object as well.

Methods with identifiers ending in =3D are a special case. They're a
special case for (what I think are) good reasons, and it makes precious
little sense whatsoever to try to use them outside the context of that
special case - such use would be nonidiomatic, and confusing. Most
people reading your code using attribute setters as chained calls would
first cringe, then run it through a code beautifier to at least put a
space around the =3D, and then visually parse it in the completely wrong
way (as an assignment) if they acted as regular method calls.
Can anyone confirm this as a rule?
=20
[snip code - speaking of which, please indent irb snippets too]

Also:

irb(main):001:0> class Foo
irb(main):002:1> def bar=3D(baz, bat)

irb(main):003:2> puts "Foo#bar=3D called with arguments baz=3D#{baz} =
and
bat=3D#{Bat}"
irb(main):004:2> return 'bing'
irb(main):005:2> end
irb(main):006:1> end
=3D> nil
irb(main):007:0>
irb(main):008:0* foo =3D Foo.new
=3D> #<Foo:0x5bf151c>
irb(main):009:0> foo.bar =3D 1, 2
ArgumentError: wrong number of arguments (1 for 2)
from (irb):9:in `bar=3D'
from (irb):9
from :0

Multiple items as the rvalue of an assignment expression are always
packed into an array as varargs:

C:\Documents and Settings\David>irb
irb(main):001:0> class Foo
irb(main):002:1> def bar=3D(baz)
irb(main):003:2> puts "Foo#bar=3D called with baz=3D#{baz.inspect}
irb(main):004:2> return :bing
irb(main):005:2> end
irb(main):006:1> end
=3D> nil
irb(main):007:0> Foo.new.bar =3D 1, 2
Foo#bar=3D called with baz=3D[1, 2]
=3D> [1, 2]

David Vallner


--------------enig91A1DD531F2507F32A81B5DE
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFdzNmy6MhrS8astoRApzOAJ9by2k/lrizysTPPaAGJiMb0tJ5pQCeO0EW
OT2teC2ZnoSq9lAeiFiXQyg=
=Wd3k
-----END PGP SIGNATURE-----

--------------enig91A1DD531F2507F32A81B5DE--
 

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