Project

General

Profile

Actions

Feature #8563

closed

Instance variable arguments

Added by sawa (Tsuyoshi Sawada) almost 11 years ago. Updated over 6 years ago.

Status:
Rejected
Target version:
-
[ruby-core:55596]

Description

Often times, people want to assign given arguments to instance variables, especially inside the method initialize:

def initialize foo, bar, buz
  @foo, @bar, @buz = foo, bar, buz
  ...
end

I propose to let method definition take instance variables as arguments so that:

def initialize @foo, @bar, @buz
  ...
end

would be equivalent as above.


Related issues 3 (1 open2 closed)

Is duplicate of Ruby master - Feature #5825: Sweet instance var assignment in the object initializerAssignedmatz (Yukihiro Matsumoto)Actions
Has duplicate Ruby master - Feature #12578: Instance Variables Assigned In parameters ( ala Crystal? )RejectedActions
Has duplicate Ruby master - Feature #12820: Shorter syntax for assigning a method argument to an instance variableRejectedActions

Updated by nobu (Nobuyoshi Nakada) almost 11 years ago

  • Category set to syntax
  • Status changed from Open to Assigned
  • Assignee set to matz (Yukihiro Matsumoto)
  • Target version set to 3.0

Updated by phluid61 (Matthew Kerwin) almost 11 years ago

Question: would this be valid?

def foo(@foo=@foo) end

Asking here, because #5825 as written only talks about initialize method, and the above construct wouldn't make sense there.

Updated by matz (Yukihiro Matsumoto) almost 11 years ago

From my POV:

def initialize(@foo, @bar)
end

does not express intention of instance variable initialization. I'd rather add a method like

define_attr_initialize(:foo, :bar)

to define a method of

def initialize(foo, bar)
  @foo = foo
  @bar = bar
end

Matz.

Updated by sawa (Tsuyoshi Sawada) almost 11 years ago

It could also be used besides initialize:

def update_something foo
   do_update_something(@foo = foo)
   ...
end

would become

def update_something @foo
   do_update_something(@foo)
   ...
end

Updated by nobu (Nobuyoshi Nakada) almost 11 years ago

phluid61 (Matthew Kerwin) wrote:

Question: would this be valid?

def foo(@foo=@foo) end

In generic,

foo = foo

is valid always.

Updated by headius (Charles Nutter) almost 11 years ago

Worth pointing out that blocks used to support this:

1.times { |@foo| ... }

Basically, it supported anything you can have on the LHS of a normal assignment:

foo.bar { |a, @b, @@c, D, e.val, f[0]| ... } 

I believe it was taken out in 1.9 because it made argument processing a lot more complicated, but then 1.9 added the ability to do multiple-assignment grouping, default values, and other masgn features to all argument lists anyway. It doesn't seem like it would be too terribly complicated to add this back in, but I wonder about the OTHER reasons that non-local variable assignment was removed from argument lists in the first place.

As for the utility of the feature, I'd like it but I can live without it. It does seem rather un-Ruby to have to declare locals and do the assignment when all you want is to set an instance variable...especially when you have a lot of instance variables.

Note also that for a trivial initialize, where the only line is a multiple-assignment to set instance variables, every initialize call pays the cost of creating an Array for the masgn (see my rejected request in https://bugs.ruby-lang.org/issues/6668).

def initialize(a, b, c, d, e)
  @a, @b, @c, @d, @e = a, b, c, d, e # creates transient array every time
end

Updated by Anonymous almost 11 years ago

@matz (Yukihiro Matsumoto): If define_attr_initialize is an option, then there is a question of named / ordered qualifier, either as:

define_attr_initialize :foo, :bar, as: :named
define_attr_initialize :baz, :quux, as: :ordered

or as (and I like this second option better):

attr_init_named foo: nil, bar: nil
attr_init_ordered :baz, :quux

Both of these would obviously stand for (using @sawa's notation):

def initialize( @baz, @quux, @foo: nil, @bar: nil )
      ...

Besides that, I feel that attr_reader, attr_writer, attr_accessor could use:

attr_reader :foo, :bar, init: :named
attr_reader :baz, :quux, init: :ordered

Which also brings to my mind, that now that we have named args firmly in place, following
syntactic flavors of Module#attr_... beg to exist:

attr_reader foo: 42, bar: 43  # specifying starting values explicitly

Updated by phluid61 (Matthew Kerwin) almost 11 years ago

boris_stitnicky (Boris Stitnicky) wrote:

Which also brings to my mind, that now that we have named args firmly in place, following
syntactic flavors of Module#attr_... beg to exist:

attr_reader foo: 42, bar: 43  # specifying starting values explicitly

If you propose this as a feature, I will +1 it. Also I have some questions about it which probably should not pollute the current feature request.

Updated by Anonymous almost 11 years ago

If you propose this as a feature, I will +1 it. Also I have some questions about it which probably should not pollute the current feature request.

I have proposed is as #8564.

Updated by nobu (Nobuyoshi Nakada) about 8 years ago

  • Description updated (diff)
Actions #11

Updated by nobu (Nobuyoshi Nakada) almost 8 years ago

  • Has duplicate Feature #12578: Instance Variables Assigned In parameters ( ala Crystal? ) added

Updated by Eregon (Benoit Daloze) almost 8 years ago

Anonymous wrote:

@matz (Yukihiro Matsumoto): If define_attr_initialize is an option, then there is a question of named / ordered qualifier, either as:

define_attr_initialize :foo, :bar, as: :named
define_attr_initialize :baz, :quux, as: :ordered

You could have simply:

define_attr_initialize :baz, :quux, foo: nil, bar: nil

But this starts to look much like a macro to me.

One problem with the proposed define_attr_initialize is once some extra behavior needs to be added to initialize,
there is no choice but to desugar everything (write ivar assignments manually).
The original proposition does not have this issue.

IMHO many constructors usually need some custom behavior after some time,
and so paying the "cost" upfront of doing manual assignments is worth it in many cases.

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) almost 8 years ago

Even if define_attr_initialize would accept a block to enhance the initializer besides initializing instance variables it doesn't support default arguments. I'd certainly prefer the original proposal which I find more useful. It's not obvious what array.each(&:to_i) does either but people get used to it because it's handy. I think it's the same about this proposal although it's quite familiar for people using this feature in other languages like CoffeeScript and Crystal among others.

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) almost 8 years ago

Also it doesn't handle variable arguments, extra options that shouldn't be assigned to instance variables or &block.

Actions #15

Updated by nobu (Nobuyoshi Nakada) over 7 years ago

  • Has duplicate Feature #12820: Shorter syntax for assigning a method argument to an instance variable added

Updated by marcandre (Marc-Andre Lafortune) over 6 years ago

matz (Yukihiro Matsumoto) wrote:

Arguments are giving names to passed values, which is different from attribute (instance variables) initialization. I think they should be separated clearly.

This may be a valid theoretical distinction, but what does this change in practice?

I feel there would be about no additional cognitive load if this was accepted. It is most probably the most requested feature

This is not surprising, since a majority of initialize with arguments is doing that!

And for most of the cases, only initialize needs this kind of initialization.

Indeed. This doesn't change the case that a majority of initialize methods needs this kind of initialization.

Here's an example Rails app (which I didn't write): https://github.com/WikiEducationFoundation/WikiEduDashboard/search?utf8=%E2%9C%93&q=def+initialize&type=
A gem I'm using a lot these days: https://github.com/whitequark/parser/search?utf8=%E2%9C%93&q=def+initialize&type=

Since keyword arguments, I feel that's even more true, since Struct is no longer as useful.

My idea is https://bugs.ruby-lang.org/issues/8563#note-3

With all due respect, this is not a good idea and it can't solve the issue. As was pointed out, there are many more cases that it doesn't handle:

  • keyword arguments (that's absolutely major!)
  • defaults
  • dealing with other arguments
  • doing extra code

I'd also mention the additional cognitive load involved.

Note that it has always been trivial to implement define_attr_initialize in pure Ruby. I've never seen such code though.

Take the top 100 gems, a few web apps, make an inventory of the initialize with parameters. I'm betting that the vast majority of these methods would be simplified by allowing instance variable arguments. I doubt that define_attr_initialize would help any but a small minority.

Updated by ko1 (Koichi Sasada) over 6 years ago

Not so serious idea.

class Binding
  def set_attributes(attrs = self.local_variables)
    attrs.each{|attr|
      self.receiver.instance_variable_set(:"@#{attr}", self.local_variable_get(attr))
    }
  end
end

class C
  def initialize a, b
    binding.set_attributes
  end
end

p C.new(1, 2) #=> #<C:0x000001f30a8b95d0 @a=1, @b=2>

Updated by matz (Yukihiro Matsumoto) over 6 years ago

  • Status changed from Assigned to Rejected

My opinion has not been changed from https://bugs.ruby-lang.org/issues/8563#note-3
I am strongly against code like

def initialize(@foo, @bar)
end

I don't care about define_attr_initialize not being "the solution".

Matz.

Updated by ko1 (Koichi Sasada) over 6 years ago

Oops.

class Binding
  def set_attributes(attrs = self.eval('method(__method__).parameters.map{|t, v| v}'))
    attrs.each{|attr|
      self.receiver.instance_variable_set(:"@#{attr}", self.local_variable_get(attr))
    }
  end
end

class C
  def initialize a, b
    x = y = nil
    binding.set_attributes
  end
end

p C.new(1, 2) #=> #<C:0x000001f30a8b95d0 @a=1, @b=2>
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0