Project

General

Profile

Actions

Feature #5825

open

Sweet instance var assignment in the object initializer

Added by goshakkk (Gosha Arinich) almost 13 years ago. Updated about 1 year ago.

Status:
Assigned
Target version:
-
[ruby-core:<unknown>]

Description

I'm very excited about this feature in CoffeeScript, and think it might be a nice-to-have thing in Ruby 2.0.

That's how I think it would look like:

class Me
  def initialize(@name, @age, @location); end
end

So we can declare @variables in the initializer method parameters definition to avoid assigning instance variables from method arguments by hand, like:

class Me
  def initialize(name, age, location)
    @name = name
    @age = age
    @location = location
  end
end

Want to hear what do you guys think, does that feature worth being included in 2.0?


Related issues 7 (4 open3 closed)

Related to Ruby master - Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructorOpenActions
Has duplicate Ruby master - Feature #8563: Instance variable argumentsRejectedmatz (Yukihiro Matsumoto)Actions
Has duplicate Ruby master - Feature #12023: Allow ivars to be used as method argumentsOpenActions
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
Has duplicate Ruby master - Feature #15192: Introduce a new "shortcut assigning" syntax to convenient setup instance variablesAssignedmatz (Yukihiro Matsumoto)Actions
Has duplicate Ruby master - Feature #19898: Special syntax for instance variable assignment OpenActions
Actions #1

Updated by lisovskyvlad (Vlad Lisosvky) almost 13 years ago

I like it. No stupid assigns.

Updated by rue (Eero Saynatkari) almost 13 years ago

Would be nice, and should be able to coexist with normal parameters:

def foo(bar, @baz, quux = @moomin)
  
end

And so on. What about splat- and block arguments? It gets a little ugly:

def foo(bar, *@baz, &@quux)
  
end

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

+1 - too common use case

Updated by shyouhei (Shyouhei Urabe) almost 13 years ago

I liked this 1.8-specific trick:

define_method(:intialize){|@foo, @bar|}

but it was abondoned for any reason.

Actions #5

Updated by andhapp (Anuj Dutta) almost 13 years ago

+1. Common use case.

Updated by alexeymuranov (Alexey Muranov) almost 13 years ago

+1, why only initialize?

Updated by Eregon (Benoit Daloze) almost 13 years ago

I think most of the time you need to parse or check your arguments, in which case this syntax would not be practical.
Otherwise, you could use Struct to avoid the duplication:

class Me < Struct.new(:name, :age, :location)
end
Actions #8

Updated by goshakkk (Gosha Arinich) almost 13 years ago

Alexey Muranov wrote:

+1, why only initialize?

It's just too common case. It could work for other methods as well.

Actions #9

Updated by shevegen (Robert A. Heiler) almost 13 years ago

Is this even possible with the Parser?

It would eliminate a few lines of code.

Also, I am not sure if this violates the principle of least matz surprise.

If I initially see

def initialize(@name, @age, @location)

I wonder a bit, because it does not feel consistent.

Perhaps it would be different if some kind of attr* could be
used.

attr_initialize :name, :age, :location

And then the above could work. Where the name would work just
similar to an attr_writer, but different in that it assumes
default values passed to initialize to automatically go towards
those instance variables (the order must be the same of course)

Updated by naruse (Yui NARUSE) almost 13 years ago

  • Status changed from Open to Assigned
  • Assignee set to matz (Yukihiro Matsumoto)

Updated by ko1 (Koichi Sasada) over 12 years ago

  • Target version set to 2.0.0

if my memory serves me right, matz dislikes such a style of arguments.
Matz, could you explain the reason again?

I know some people like this style.

Updated by trans (Thomas Sawyer) over 12 years ago

I think it's really not such a good idea. Often you'll just end up having to redo it anyway when you finally decide to coerce and/or validate arguments to make your code more robust, e,g,

def initialize(@name, @age, @location); end

Becomes

def initialize(name, age, location)
  @name = name.to_s
  @age = age.to_i
  @location = Location.new(location)
end

Might as well write it out from the get-go in preparation.

Updated by ko1 (Koichi Sasada) about 12 years ago

  • Target version changed from 2.0.0 to 2.6

I changed target to next minor.
I think someone who want to introduce it need to persuade matz.

Updated by Anonymous almost 12 years ago

Well... I like the sweetness... But to have such a feature working syntactically from
inside of #initialize method, but not from other methods... I don't know.

It's not like this is my suggestion, but since it is up for discussion, let me try to
straighten this proposal according to my own thinking:

These "attributes on steroids" are a thing to be done not at the level of the #initialize
method, but at the level of the module, along with #attr_accessor and friends.

Imho, it would be necessary to update #attr_accessor & friends to accept :autoinit
named argument:

attr_reader :name, :age, autoinit: true

Now there are two flavors of this candy, one with ordered arguments, one with named.
So we could have to specify it:

attr_reader :name, autoinit: :ordered
attr_reader :age, autoinit: :named

I'm sure you know what I mean here. Default option could be eg. :named.

Also, #autoinit method, or rather, #autoinit_named, #autoinit_ordered,
would have to be added to the Module, for those times, when we want to
autoinit, but don't want the reader/writer/accessor:

autoinit_named :age
autoinit_ordered :name

Afaik, current #attr_accessor & friends work by defining instance methods on
the module. How #autoinit should work, is a question. Two possibilities come
to my mind:

  1. By patching #initialize method.

  2. By creating and including a mixin patching #new class method, that would
    set the appropriate instance variables right after creating a new instance,
    in effect something like this:

    module AgeNamedArgAutoinit
      def new *args, &block
        named_args = args.extract_options!
        age = named_args.delete :age
        modified_args = args + named_args.empty? ? [] : [named_args]
        new_instance = super *modified_args, &block
        new_instance.instance_variable_set :@age, age
      end
    end
    
    module NameOrderedArgAutoinit
      def new *args, &block
        name = args.shift
        new_instance = super *args, &block
        new_instance.instance_variable_set :@name, name
      end
    end
    
    class MyClass
      include AgeNamedArgAutoinit
      include NameOrderedArgAutoinit
    end
    

Now MyClass.new( "John Smith", :whatever, age: 35, other_stuff: :whatever ) should
behave in the expected way.

Again, I have not come up with this proposal, I do not give +1 or -1 to it,
I am only trying to iron it to be more consistent, leaving the decision to others.

Updated by phluid61 (Matthew Kerwin) almost 12 years ago

alexeymuranov (Alexey Muranov) wrote:

+1, why only initialize?

I agree. Is there a reason not to specify something other than a local variable as the receiver for a method parameter?

For example:

# precondition: ???
# postcondition: updates @instance_var and $global_var
def some_thing(local_var, @instance_var, $global_var)
  # ...
end

I know it's not safe, in that it makes it quite easy to shoot one's self in the foot, but I don't know that it's necessarily a bad thing unless someone tells me it is.

Updated by sikachu (Prem Sichanugrist) over 11 years ago

I think this is a good feature, so I'd like to support this (and possibly, provide a patch for this)

Reading from all the comments, I saw that someone has some concern about having this feature on another method definition (not just initialize) as well. I think we should implement it for any type of method definition if that's going to make the code cleaner, but the main focus here is for the initialize method.

As per comment 12, I think it's OK to start up with def initialize(@foo, @bar, @baz) and then refactor it if you need to perform any method on those arguments before store it to an instance variable.

I think adding support for only local and instance variables make sense, and I think we need to make sure that we're not supporting global variable like in comment 15.

Anyway, the benefit I'm seeing here is that we're not cluttering the initializer with all those obvious instance variable assignments. I also think it's a good syntactic sugar and it make sense after I saw all the usage from CoffeeScript. I hope I can help to make this happen in the next minor.

Updated by nobu (Nobuyoshi Nakada) over 11 years ago

  • Description updated (diff)

Updated by nobu (Nobuyoshi Nakada) over 11 years ago

  • Category set to syntax
  • Target version changed from 2.6 to 3.0

Updated by TylerRick (Tyler Rick) over 10 years ago

I would love to see this feature in Ruby. Assigning an argument to an instance variable in a constructor is something that we do in almost every constructor we write, so I think this should be made as easy and simple as possible, by adding a little syntactic sugar to the language.

I shouldn't have to repeat myself and type out each argument name 3 times in every constructor I write, for something as mundane as this:

def initialize(name, )
  @name = name
  
end

This constant repetition feels inelegant to me and goes against one of the Ruby community's most fundamental values (Don't Repeat Yourself).

This method could be simplified to simply this:

def initialize(@name, )
end

I think CoffeeScript solved this problem quite nicely. Many constructors in CoffeeScript end up being beautiful, simple one-liners!

constructor: (@name) ->

And with the rising popularity of CoffeeScript, there are going to be more and more Rubyists not only wishing for this but also expecting this same feature to exist in Ruby as well. :)


Here are a few more "votes" for this feature:

Plus various attempts at removing the duplication from assign variables in constructors, using only pure Ruby:

Updated by nobu (Nobuyoshi Nakada) over 10 years ago

  • Description updated (diff)

Tyler Rick wrote:

I think CoffeeScript solved this problem quite nicely. Many constructors in CoffeeScript end up being beautiful, simple one-liners!

constructor: (@name) ->

Sorry, it doesn look beautiful to me.

Updated by phluid61 (Matthew Kerwin) over 10 years ago

Does anyone have a link to discussions/logs that lead to the decision to remove instance/global variables from block parameters?

Actions #22

Updated by nobu (Nobuyoshi Nakada) almost 9 years ago

  • Has duplicate Feature #12023: Allow ivars to be used as method arguments added
Actions #23

Updated by nobu (Nobuyoshi Nakada) over 8 years ago

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

Updated by nobu (Nobuyoshi Nakada) about 8 years ago

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

Updated by mame (Yusuke Endoh) about 6 years ago

  • Related to Feature #15192: Introduce a new "shortcut assigning" syntax to convenient setup instance variables added
Actions #26

Updated by nobu (Nobuyoshi Nakada) over 5 years ago

  • Related to deleted (Feature #15192: Introduce a new "shortcut assigning" syntax to convenient setup instance variables)
Actions #27

Updated by nobu (Nobuyoshi Nakada) over 5 years ago

  • Has duplicate Feature #15192: Introduce a new "shortcut assigning" syntax to convenient setup instance variables added
Actions #28

Updated by nobu (Nobuyoshi Nakada) over 5 years ago

  • Has duplicate Feature #16095: 2 Features: remove (simplify) 'new' keyword and Property Shorthand added
Actions #29

Updated by nobu (Nobuyoshi Nakada) over 5 years ago

  • Has duplicate deleted (Feature #16095: 2 Features: remove (simplify) 'new' keyword and Property Shorthand)
Actions #30

Updated by naruse (Yui NARUSE) almost 4 years ago

  • Target version deleted (3.0)
Actions #31

Updated by mame (Yusuke Endoh) over 3 years ago

  • Related to Feature #17942: Add a `initialize(public @a, private @b)` shortcut syntax for defining public/private accessors for instance vars as part of constructor added

Updated by eike.send@gmail.com (Eike Send) over 2 years ago

I would really love to see native support for an initializer instance variable assignment shorthand without adding the problems when inheriting from Struct.

Actions #33

Updated by nobu (Nobuyoshi Nakada) about 1 year ago

  • Has duplicate Feature #19898: Special syntax for instance variable assignment added

Updated by matheusrich (Matheus Richard) about 1 year ago

Maybe a different proposal, but if the def initialize(@a, @b) is ugly, how about a new method similar to attr_{reader,writter} like

class User
  init_with :name, :age

  # or maybe
  attributes :name, :age
end

User.new("Matz", 21)

We could even have an option like keyword_init: true for keyword args.

On the other hand, a simple alternative is using the new Data class, which already provides this nicety:

User = Data.define(:name, :age)

User.new("matz", 21)

# or

User.new(name: "matz", age: 21)
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0