I'm constantly creating small classes, and many of them start
something like:
class Msg
def initialize(name, type, txt)
@name, @type, @txt = name, type, txt
end
# ...
end
So, could we change parameter passing slightly so that if an instance
method has a formal parameter starting with an '@' sign, the incoming
value is assigned directly to the corresponding instance variable?
Using this scheme I could write the above as:
class Msg
def initialize(@name, @type, @txt)
end
end
This would also bring methods one step closer to blocks :)
I want to thank the committee for this opportunity of presenting a
request, and wish them all good health and fortune.
Dave
I agree with that change.
This also makes an attribute writer something like:
def some_attr=(@some_attr); end
matju
-----Original Message-----
From: Dave Thomas [SMTP:Da...@PragmaticProgrammer.com]
Sent: Monday, February 26, 2001 4:21 PM
To: ruby-talk ML; ruby...@netlab.co.jp
Subject: [ruby-talk:11633] RCR: shortcut for instance variable initialization
I'm sure this will break all sorts of stuff, but just in case it
doesn't:
I'm constantly creating small classes, and many of them start
something like:
class Msg
def initialize(name, type, txt)
@name, @type, @txt = name, type, txt
end
# ...
end
So, could we change parameter passing slightly so that if an instance
method has a formal parameter starting with an '@' sign, the incoming
value is assigned directly to the corresponding instance variable?
Using this scheme I could write the above as:
class Msg
def initialize(@name, @type, @txt)
end
end
This would also bring methods one step closer to blocks :)
In message "[ruby-talk:11652] RE: RCR: shortcut for instance variable initialization"
on 01/02/27, Michael Davis <mda...@sevainc.com> writes:
|From: Dave Thomas [SMTP:Da...@PragmaticProgrammer.com]
| def initialize(@name, @type, @txt)
| end
|I like it!
I don't. I feel it makes the meaning of method arguments ambiguous.
(plus it's ugly.) But it's ok to discuss, since I am easy to change
my mind, you know.
matz.
Hmmmmmm. I can see the value for the initialize
method. But we alerady have an attribute writer
shortcut, so the latter doesn't help me.
I'm concerned that ANY method can smack your
member variables when you might have just
accidentally put the @ in out of habit from
writing it so many times elsewhere. It just makes
me a little nervous.
Could we say that initialize is the only method
to get this special treatment?
Or is there some way to have one line of code
that means "copy each of the passed parameters
into member variables with corresponding names"?
This would allow something like this:
def initialize(name, type, txt)
set_locals_to_params
... more code here...
end
Kevin
> >>>>> "D" == Dave Thomas <Da...@PragmaticProgrammer.com> writes:
>
> D> [1, 2, 3].each {|@i| }
> D> p @i #=> 3
>
> D> Here the formal arguments to a block affect non-locals. It seemed
> D> analogous.
>
> It seems, but with |@i| this a multi-assignement (NODE_MASGN) and this is
> not the case with def()
Clearly so, but ignoring the current implementation, from the outside
you could consider them similar situations. In both cases, assignment
to a 'parameter' affects external state. In the limited case of
allowing instance variables as parameters to initialize, that seems to
be a fairly simple and contained extension.
Dave
> But, I agree it ain't pretty. It's just that those parallel
> assignments are also a pain. Perhaps there's another way.
How about something that'll generate the initializers for you - so that
you could do
class Foo
initializer(:bar, :baz) do
#other stuff for initialize to do
end
end
You'd have to store the block and do an instance_eval on it, I guess,
but it shouldn't be that complicated.
D> [1, 2, 3].each {|@i| }
D> p @i #=> 3
D> Here the formal arguments to a block affect non-locals. It seemed
D> analogous.
It seems, but with |@i| this a multi-assignement (NODE_MASGN) and this is
not the case with def()
If you thing that it's analoguous then I want the syntax
[1, 2, 3].each {(@i) }
:-)))
Guy Decoux
D> Clearly so, but ignoring the current implementation, from the outside
D> you could consider them similar situations.
^^^^^^^^^^^^^^^^^^
You have forgotten :-)
a = 12
def toto(a = 24)
end
a = 12
[24].each {|a| }
Guy Decoux
But
[1, 2, 3].each {|$i| }
p $i #=> 3
[1, 2, 3].each {|@i| }
p @i #=> 3
Here the formal arguments to a block affect non-locals. It seemed
analogous.
But, I agree it ain't pretty. It's just that those parallel
assignments are also a pain. Perhaps there's another way.
Regards
Dave
> How about something that'll generate the initializers for you - so that
> you could do
>
> class Foo
> initializer(:bar, :baz) do
> #other stuff for initialize to do
> end
> end
e.g:
class Class
def initializer(*args, &b)
define_method(:__init_proc) {b}
params = args.join(", ")
vars = args.collect{|a| "@#{a}"}.join(", ")
class_eval <<-EOS
def initialize(#{params})
#{vars} = #{params}
instance_eval &__init_proc
end
EOS
end
end
> >>>>> "D" == Dave Thomas <Da...@PragmaticProgrammer.com> writes:
>
> D> Clearly so, but ignoring the current implementation, from the outside
> D> you could consider them similar situations.
> ^^^^^^^^^^^^^^^^^^
>
> You have forgotten :-)
No, I hadn't forgotten local variables. I was simply pointing out that
there is a precedent for formal arguments affecting values outside
their apparent scope.
Dave
It's a simple extension module that allows an "advice" method to be added
to any class object, that controls (executes code before and after, and
chooses whether to call) any instance methods flagged as advised. Unlike
most of the other method-wrapping stuff, it does it in a tiny bit of C
code rather than with pre-processor style code generation.
For example:
require 'advice'
class Foo
def Foo.advice(obj, method, args)
if(args[0] < 0)
puts "don't like negative numbers"
else
result = yield
puts "result: " + result
end
end
def subtract(x, y)
x - y
end
def add(x, y)
x + y
end
end
foo = Foo.new
foo.subtract(3,2) #=> 1
foo.add(-1, 1) #=> 0
Foo.advise(:subtract)
Foo.advise(:add)
foo.subtract(3,2) #=> 1
#=> result: 1
foo.add(-1, 1) #=> nil
#=> don't like negative numbers
I mean it mostly as an example, and as a foundation on which to build libs
like AspectR, but it could come in handy all by itself for some things,
too.
Avi
I do not understand exactly the nature of the objections. It seems to me
to be a fairly innocuous notational shortcut. If I understand correctly the
proposal, it is just that when you have a method
def f(a,@b,c)
...
end
it is understood as exactly equivalent to
def f(a,dummy,c)
@b=dummy
...
end
unless I missed something?
What is ugly? That by analogy someday someone might ask
def f(a,$b,c)
...
end
to be a shortcut for
def f(a,dummy,c)
$b=dummy
...
end
I must confess I don't see the problem here either. Please bring light.
Best regards,
Jean MICHEL
In message "[ruby-talk:11675] Re: RCR: shortcut for instance variable initialization"
on 01/02/27, Jean Michel <jmi...@schur.institut.math.jussieu.fr> writes:
|I do not understand exactly the nature of the objections. It seems to me
|to be a fairly innocuous notational shortcut. If I understand correctly the
|proposal, it is just that when you have a method
|
| def f(a,@b,c)
| ...
| end
|
|it is understood as exactly equivalent to
|
| def f(a,dummy,c)
| @b=dummy
| ...
| end
|
|unless I missed something?
It is what Dave wanted, yes. I personally feel like that method
paremeters are names to the values passed, not hidden assignment,
regardless of how implementation works. So that side effect,
i.e. altering instance/global variables, are "too much" for them,
I think.
matz.
I do not like the idea. Adding magic just to save typing is only a good
idea if the magic is beautiful. This magic is not.
IMHO, typing is not so bad. Most people type faster than they think
anyway (Stephen Hawkins is probably an exception.)
If you do need to save some typing, one idea (inspired by Mathieu Bouchard)
is to create a function that assigns local variables to instance varaibles.
def locals_to_iv(bin)
eval("local_variables", bin).each {|v| eval("@#{v}=#{v}", bin)}
end
Now you can just write
class Test
def initialize(name, type, txt)
locals_to_iv(binding)
...
Of course, if the goal is to save typing, you may want to name this
function "l" instead ;)
// Niklas
> > class Msg
> > def initialize(@name, @type, @txt)
> > end
> > end
>
> I do not like the idea. Adding magic just to save typing is only a
> good idea if the magic is beautiful. This magic is not.
I agree it isn't pretty, and that the idea is probably dead. But let
me explain my motivation.
I didn't suggest this to save typing: that's rarely a good reason to
change something. Instead, I suggested it because I feel that the
other way
def fred(param1, param2, anotherparam)
@param1, @param2, @anotherparam = param1, param2, anotherparam
...
has a couple of inherent problems.
First, it's noisy. It clutters up the source, and cuts down on
communication of _real_ semantic content.
Second, it's error prone. There are two effective duplications of
'param1' in the above, and as you may know, I don't like duplication :)
Now, Ruby already has many convenience facilities built in (things
like attr_xxx) which cut down the clutter in a class definition and
eliminate some duplication at the same time. I was just angling to add
another. I personally didn't like the syntax, but it seemed consistent
with the handling of block parameters, and was unambiguous.
I'd still like to find a way to do this without duplicating the names.
Dave
> r2...@mao.acc.umu.se (Niklas Frykholm) writes:
>
> > > class Msg
> > > def initialize(@name, @type, @txt)
> > > end
> > > end
> >
> > I do not like the idea. Adding magic just to save typing is only a
> > good idea if the magic is beautiful. This magic is not.
>
> I agree it isn't pretty, and that the idea is probably dead. But let
> me explain my motivation.
[...]
> I'd still like to find a way to do this without duplicating the names.
I have what is probably a weird idea, but anyway, have a look:
class Msg
def initialize(name@, type@, txt@)
# Here, the variables would be available *without* the '@'
# e.g., "puts name"
# and the assignments "@name = name", etc., would be automatic
end
end
The point would be to trigger the assignment, but without using
exactly the same name as the instance variables themselves. (I do
find (@name, @type, @txt) confusing.)
David
--
David Alan Black
home: dbl...@candle.superlink.net
work: blac...@shu.edu
Web: http://pirate.shu.edu/~blackdav
> @param1, @param2, @anotherparam = param1, param2, anotherparam
> has a couple of inherent problems.
>
> First, it's noisy. It clutters up the source, and cuts down on
> communication of _real_ semantic content.
>
> Second, it's error prone. There are two effective duplications of
> 'param1' in the above, and as you may know, I don't like duplication :)
This is not sufficient?
class Module
def auto_iv_init( nm, *params )
name = nm.id2name
alias_method "_real_#{name}", nm
remove_method nm
module_eval %~
def #{name}( *args, &block )
#{params.collect {|n| '@' + n.id2name }.join(',')} = args
_real_#{name}( &block )
end
~
end
end
class C
def foo
p @param1
p @param2
p @anotherparam
end
auto_iv_init :foo, :param1, :param2, :anotherparam
end
C.new.foo( * %w< a b c > )
At least this code does not create duplications (except method name).
Weak points are:
1. auto_iv_init() call must be placed AFTER definition.
FYI we can resolve this problem by using method_added()
and class variable.
2. better name is required.
Minero Aoki
> This is not sufficient?
It's clever, but I think it hides the programmer's intention. One of
the things I want to achieve is better communication.
So far, I like David Black's 'def foo(hoge@)' best.
Dave
> I have what is probably a weird idea, but anyway, have a look:
> class Msg
>
> def initialize(name@, type@, txt@)
>
> # Here, the variables would be available *without* the '@'
> # e.g., "puts name"
> # and the assignments "@name = name", etc., would be automatic
>
> end
> end
>
>
> The point would be to trigger the assignment, but without using
> exactly the same name as the instance variables themselves. (I do
> find (@name, @type, @txt) confusing.)
Would you want it to work like this:
def blah(name@, type$, txt)
where @name $type and txt
are created?
personally I like
def blah(@name,$type,txt)
It seems straight forward to me, but perhaps I'm strange, or
I'm missing something.
-joe
/duck
OK, ok, on to a more serious note. I don't have a good
suggested syntax, but how about we generalize the desire
for a second and think about where that leads? If you
have written your attr methods, your temporary variables
actually all correspond pretty closely to methods that
you have already. For instance:
attr_accessor :foo
will have created methods foo and foo= for public access to
@foo.
Am I right that you would be happy if you could just call
foo= directly?
I can't think of what it should look like, but there might
be a number of uses for a syntax that allows you to easily
preprocess your argument list in a variety of ways. (One
idea bubbling in my brain is named function parameters.)
So can anyone think of more uses for the idea or (far more
importantly) what a reasonable syntax might be?
Cheers,
Ben
_________________________________________________________________
Get your FREE download of MSN Explorer at http://explorer.msn.com
I wouldn't advocate anything like this for global variables.
You'd get ugly scope things, like:
class Thing
def blah(var$)
end
end
t = Thing.new
t.blah(123)
# now there's a global called $var -- no good
As for the case of instance variables, and @var vs. name@:
When I see @var, I think: the instance variable @var. With the "def
thing(@var)" construct, I have to go through an extra layer of
realizing that it isn't exactly @var. var@ occurred to me as a way to
signal that there's something "extra" going on, while not using the
actual names of any instance variables. And there would be a local
variable called name (since you might want to do something with it
other than assign it to @var). So this:
def blah(var@)
end
would be precisely equivalent to:
def blah(var)
@var = var
end
which fits in with Dave's original goal.
(Mind you, as I recall Matz was not enthusiastic about the whole idea.
Then again, it hasn't appeared on DeniedRequests at rubygarden yet :-)
> class Whatever
>
> def will_be_rounded=(number)
> raise 'Not a number' unless kind_of? Numeric
> @will_be_number = number.round
> end
>
> def initialize(will_be_rounded@)
> ...
> end
> attr_accessor :will_be_rounded
> end
>
> does a call to initialize invoke ``@will_be_rounded=''
> or```will_be_rounded=''?
There is no such method as '@will_be_rounded='. The attr_accessor
call will overwrite the method you defined with it's own:
class Dave
def val=(n)
puts "in my val"
@val = 123
end
attr_accessor :val
end
d = Dave.new
d.val = 432
p d.val #=> 432
> Ps. I sometimes wonder if Ruby could have done without
> variables at all (I believe this) and only use attributes
> but this is a different matter.
Attributes are simply accessor functions to instance variables.
Dave
you make one of the most interesting comments
(for my taste;-). Actually there is a little
problem if have something like
class Whatever
def will_be_rounded=(number)
raise 'Not a number' unless kind_of? Numeric
@will_be_number = number.round
end
def initialize(will_be_rounded@)
...
end
attr_accessor :will_be_rounded
end
does a call to initialize invoke ``@will_be_rounded=''
or```will_be_rounded=''?
> I can't think of what it should look like, but there might
> be a number of uses for a syntax that allows you to easily
> preprocess your argument list in a variety of ways. (One
> idea bubbling in my brain is named function parameters.)
>
> So can anyone think of more uses for the idea or (far more
> importantly) what a reasonable syntax might be?
I am pretty neutral about this RCR but one way might
be the introduction of additional ``#initialize's'' -
maybe something like
def initialized(initialize_me; dont_initialize_me)
...
end
but you still have the problem about the ambiguity of the
``initialize_me='' invocation.
Ps. I sometimes wonder if Ruby could have done without
variables at all (I believe this) and only use attributes
but this is a different matter.
[...]
Christoph
it should have been ``@will_be_rounded = number.round''
the question in my mind was if the line
``@will_be_rounded = number''
is created, or the function
``will_be_rounded=(number)''
is called (but you can always cook up a convention;-)
> call will overwrite the method you defined with it's own:
>
> class Dave
> def val=(n)
> puts "in my val"
> @val = 123
> end
> attr_accessor :val
> end
>
> d = Dave.new
> d.val = 432
> p d.val #=> 432
>
>
> > Ps. I sometimes wonder if Ruby could have done without
> > variables at all (I believe this) and only use attributes
> > but this is a different matter.
>
> Attributes are simply accessor functions to instance variables.
I know this,
I was thinking that would have been forced
into writing (basically reviving standard
variable declaration I still prefer;-)
def bla(right)
attr_accessor :left
left = right
end
Christoph
[..]
FWIW, I think I like this suggestion best so far.
Thanks,
Guerry
--
Guerry Semones, gsem...@treenleaf.com, http://www.treenleaf.com
Code Ruby (http://www.ruby-lang.org)
Be Extreme (http://www.extremeprogramming.org)
Be Pragmatic (http://www.pragmaticprogrammer.com/ppbook)
Ride the Cluetrain (http://www.cluetrain.com)
> I was thinking that would have been forced
> into writing (basically reviving standard
> variable declaration I still prefer;-)
>
> def bla(right)
> attr_accessor :left
> left = right
> end
I mend to so say that since variables are nothing
but constant function calls (who's (calling) value
can be changed via assignment - i.e. a call to an
attr_writer) why have the two distinct concept of
variables and functions around at all?
As for initialized function calls my idea was to
introduce a magic syntax on the function name lets
say
def fun@(a, b, c)
...
end
which would mean that the lines
def fun(a,b,c)
@a,@b,@c = a,b ,c
...
end
are created or restrict it to ``initialize'' via
``initialized''. This is not as flexible David's
proposal (which is pretty good IMO), but I am
not very opinionated on the matter anyway ...
Christoph
I'm ok with that approach, but personally would
like to see it ONLY work for the initialize
method.
Here's another off-the-wall idea. I don't think I
like it either, but maybe it will spark another
idea.
class Goofy
attr :x, :y, :more
auto_init :x, :y
def initialize(text, x, y, more)
# at this point, @x and @y have been set to x
and y
# @text does not exist, and @more is not
initialized yet
end
end
Or a variation would be:
class Goofy
attr :x, :y, :more
def initialize_auto(text, x, y, more_stuff)
# at this point, @x and @y have been set to x
and y
# @text does not exist, and @more is not
initialized yet
end
end
Hmmm. Yeah, I think I like the variable@ better
than either of these.
Kevin
You can already write proc {|@a,@b,@c|}. Do you think that feature
should be removed?
matju
Yes, but you can also write:
proc {|(a,b),c[2]|}
If we want to add functionality to parameter assignment, I think the
best choice is not to add magic for special cases like "@a", but instead
go all the way and remove the differences between parameter assignment
and ordinary assignment altoghether.
This would mean allowing function declarations such as:
def f((a,b),c[2])
But also that ordinary assignment would have to be changed to support
the functionality of parameter assignment, such as
(a, b=2, c=4) = *[3, 3]
# would set a = 3, b = 3, c = 4
and
(a, &b) = 3 {|x| p x}
# would set b = proc {|x| p x}
This would make the language more orthogonal, but would probably
require major changes in the implementation. Also I'm not sure
that it is such a good idea.
I'm used to thinking of assignment and parameter passing as different
concepts, but that might just be prejudice. What do you others
say?
// Niklas
I know. I was just observing that it happened that with that feature there
is only a difference of two characters between the expanded version of a
writer and the one of a reader.
> I'm concerned that ANY method can smack your
> member variables when you might have just
> accidentally put the @ in out of habit from
> writing it so many times elsewhere.
You may also smack your program in so many other ways. Imagine, you can
put * and & in lots of parameter lists already.
> Could we say that initialize is the only method
> to get this special treatment?
Certainly not. Neither the parser nor the interpreter "know" that
#initialize even exists. Why should they?
> Or is there some way to have one line of code
> that means "copy each of the passed parameters
> into member variables with corresponding names"?
> This would allow something like this:
> def initialize(name, type, txt)
> set_locals_to_params
To me, the only point of @ in parameter lists was to make methods and
procs a little more alike. It seems that not many people have realized
this.
matju
Maybe that is my Cartesian (I am a French mathematician) leanings, but
I find this idea excellent. Could it be an RCR?
Best regards,
Jean MICHEL