2

In Ruby, like many languages, a method's arguments are not automatically assigned as instance variables.

This works:

def initialize(a)
  @a = a
end

This doesn't:

def initialize(@a)
end

In CoffeeScript, for example, this works:

constructor: (@name) ->

There are a lot of other syntactic sugar in Ruby, such as the ||= operator, the unary & on symbols, etc. Is there any reason, technical or otherwise, why this sugar isn't part of the design?

Edit

The scope of the question is not limited to initialize.

In CoffeeScript you can also do

class Foo
  baz: (@bar) ->
Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168
gmalette
  • 2,439
  • 17
  • 26

4 Answers4

3

The reason is that Matz decided it this way, at least for now. There's an open feature request with quite a few supporters trying to convince Matz.

Note that often you should consider inheriting from Struct.new(:name, ...) which will define a default constructor for you setting the corresponding instance variables, as well as accessors, ==, eql?, hash, etc...

Marc-André Lafortune
  • 78,216
  • 16
  • 166
  • 166
2

In Ruby 1.8 and earlier, block arguments use assignment semantics instead of argument binding semantics, so you can do this:

define_method(:initialize) do |@a| end
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • Wow that's cool! So far as you know, was this a bug or a feature? – gmalette Apr 18 '13 at 01:42
  • 1
    That seems like a terribly bad idea, so if they removed it, we're all better off. – tadman Apr 18 '13 at 01:52
  • I'm not saying it's a nice feature, especially not on blocks. I'd just like to know why it worked. – gmalette Apr 18 '13 at 02:05
  • 1
    @gmalette: Like I said, before Ruby 1.9 block argument passing was done using assignment semantics. IOW: Passing arguments to blocks was treated exactly as if you had assigned the arguments to the parameters. You could even do something like this: `define_method(:initialize) do |foo.bar| end`, which would call the `foo.bar=` method every time the block is called! The reason for this (I presume) was that you want things `some_hash.each do |key, value| end` to work, even though `Hash#each` doesn't actually `yield` two arguments but one argument which is a two-element `Array`. – Jörg W Mittag Apr 18 '13 at 08:42
  • 1
    With parallel assignment, this works perfectly: `key, value = [:foo, 42]` will assign `:foo` to `key` and `42` to `value`, which is what you want. For block arguments, you want the same thing, so it makes sense to simply specify argument binding as assignment. However, there are a lot of corner cases, for example, you cannot assign blocks to variables, ergo you can't pass blocks to blocks. In 1.9, this was fixed so that now, block arguments use the same argument binding semantics as methods, with some added special casing for passing more or fewer arguments as well as a single `Array`. – Jörg W Mittag Apr 18 '13 at 08:46
  • 1
    Cool trivia, even though it doesn't answer the question. – Marc-André Lafortune May 16 '13 at 05:48
1

In many languages this is not allowed, nor it is in Ruby. In C++ for example you can't assign member variables directly in the constructor's argument list (using the initializer list will still require you to copy arguments to member variables manually).

I personally think its cleaner to have @a = a in the constructor body than initialize(@a) injected directly into the object member variables.

Shoe
  • 74,840
  • 36
  • 166
  • 272
  • 2
    The interface should not go into implementation details, and as such, specifying things like `@a` is contrary to convention. Some languages straight-out disallow it, as you say, like C++. – tadman Apr 18 '13 at 01:53
  • @tadman, You have expressed the problem better in 1 line than I've done in an entire answer. +1 – Shoe Apr 18 '13 at 01:56
  • @tadman Agreed, but is there really a way to separate interface from implementation in Ruby? – gmalette Apr 18 '13 at 02:04
  • The "interface" is a conceptual thing, no hard boundary exists in Ruby as it might in C where headers are often in a separate file, but it's something to be respected. If you start to blur these concerns, you get into trouble. Think of it from the perspective of documentation where your method signature, arguments and all, serves as a reference for how to use it, not how it does anything. – tadman Apr 18 '13 at 02:57
  • The existence of attr_reader/_writer/_accessor already blurs the interface-implementation boundary. If I see that `attr_* :foo` is defined, it very strongly suggests that `@foo` is used underneath. – Matty K Mar 14 '14 at 02:13
0

Not sure what you're after. Ruby before Ruby 2 had no named arguments, so to do what you're describing you'd need to pass both name and value. But in that case you might as well pass a hash, which is trivial to parse, as here: https://stackoverflow.com/a/12763031/341994

Community
  • 1
  • 1
matt
  • 515,959
  • 87
  • 875
  • 1,141