Project

General

Profile

Actions

Feature #8839

open

Class and module should return the class or module that was opened

Added by headius (Charles Nutter) over 10 years ago. Updated about 8 years ago.

Status:
Assigned
Target version:
-
[ruby-core:56890]

Description

With the change for https://bugs.ruby-lang.org/issues/3753, "def" forms now return the symbolic name of the method defined. Because class and module bodies just return the last expression in their bodies, this means they will now usually end up returning a symbol for the last method defined. This does not seem useful or correct.

I think class and module should return a reference to the class or module just opened. This would make the return value useful and consistent.


Related issues 1 (0 open1 closed)

Related to Ruby master - Feature #11905: Change the 'class' keyword to return a symbolClosedActions

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

When thinking of potential incompatibilities, the only case I could think of where I'd ever used the result of class or module was:

class << foo
  self
end

Ironically, the proposed change would not introduce an incompatibility in this case :-)

Updated by Anonymous over 10 years ago

+1

Updated by headius (Charles Nutter) over 10 years ago

So...if there's no objections, can we get this into 2.1? I think with the new def return value change it really needs to happen.

Updated by nobu (Nobuyoshi Nakada) over 10 years ago

No, class and def are evaluated in different timings.
This proposal makes no sense.

Updated by Anonymous over 10 years ago

nobu: I don't understand what you mean by "evaluated in different timings". Could you please explain?

I think this proposal is a good idea. For example, this makes no sense:

class A
  def foo
  end
end # => :foo

But this makes more sense:

class A
  def foo
  end
end # => A

Updated by mame (Yusuke Endoh) over 10 years ago

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

How useful is this proposal?
I think we should not change anything without consideration of use case.

--
Yusuke Endoh

Actions #8

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 10 years ago

I've been wondering the same thing since I saw this ticket being created...

Actions #9

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 10 years ago

Actually, I don't understand even why returning a symbol from method definition is useful...

Updated by headius (Charles Nutter) over 10 years ago

mame (Yusuke Endoh) wrote:

How useful is this proposal?
I think we should not change anything without consideration of use case.

One use:

my_class = class Foo
  ...
end

We can get the reference to a class being created immediately without adding "self" at the end. It also brings some equivalence with my_class = Class.new.

Another:

class Foo
  def self.init
    @foo = Foo.new
  end
end.init

The use cases I can think of are all fairly subtle, but I think they're valid.

Ultimately, the biggest reason I think this should happen is because class Foo; def bar; end; end returning :bar in 2.1 makes very little sense.

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 10 years ago

I see now. I really tried a few times to get something like your second example to work:

class MyProcessor
 ...
end.new(params).process

I'd usually use such pattern in small scripts since methods are not hoisting like functions declarations in JavaScript and I prefer to write code top-down, so I enclose them in a class.

Updated by mame (Yusuke Endoh) over 10 years ago

headius (Charles Nutter) wrote:

One use:

my_class = class Foo
  ...
end

We can get the reference to a class being created immediately without adding "self" at the end.

I fail to see why it needs to be a local variable.
Why don't you use Foo instead of my_class?

Another:

class Foo
  def self.init
    @foo = Foo.new
  end
end.init

The use cases I can think of are all fairly subtle, but I think they're valid.

It is very arguable if the new idiom should be encouraged.
Personally, I don't like to see such a code. Rather, I prefer:

class Foo
  def self.init
    @foo = Foo.new
  end
  self.init
end

or even:

class Foo
  @@foo = Foo.new
end

Ultimately, the biggest reason I think this should happen is because class Foo; def bar; end; end returning :bar in 2.1 makes very little sense.

IMHO, most of casual users do not care too much for a return value.
For example, we often see the following type of code:

def foo
  internal_ary = []
  # ... building internal_ary ...
  internal_ary.each do |elem|
    # ... using elem ...
  end
end

Obviously, the author of this method has no intent to return internal_ary.
But, few people writes a code that returns nil explicitly to hide the value.

def foo
  internal_ary = []
  # ...
  internal_ary.each do |elem|
    # ...
  end
  nil
end

--
Yusuke Endoh

Updated by headius (Charles Nutter) over 10 years ago

mame (Yusuke Endoh) wrote:

headius (Charles Nutter) wrote:

One use:

my_class = class Foo
  ...
end

We can get the reference to a class being created immediately without adding "self" at the end.

I fail to see why it needs to be a local variable.
Why don't you use Foo instead of my_class?

How about this:

meta = class << self; end

It is very arguable if the new idiom should be encouraged.
Personally, I don't like to see such a code. Rather, I prefer:

class Foo
  def self.init
    @foo = Foo.new
  end
  self.init
end

or even:

class Foo
  @@foo = Foo.new
end

I respect your opinion, but I fail to see why your way is better than mine.

Ultimately, the biggest reason I think this should happen is because class Foo; def bar; end; end returning :bar in 2.1 makes very little sense.

IMHO, most of casual users do not care too much for a return value.

Users not caring about return value is not a good reason to return a nonsensical value. Returning the last method name defined in a class body makes no sense. Returning the class that was just defined may not make sense to you, but it makes sense to me and several others who have commented here.

Updated by robertgleeson (Robert Gleeson) over 10 years ago

I agree that returning the class or module makes sense (to me).
I'd also like to see "def foo" return a (Unbound)Method instead of a Symbol.
it seems like that'd also make more sense (not to derail this conversation). A (Unbound)Method is much more useful.
I saw headius mention you can say:

class Foo
  instance_method def foo
    end # => UnboundMethod.
end

...but seems like a nice default.

+1 to the proposal.

Updated by mame (Yusuke Endoh) over 10 years ago

headius (Charles Nutter) wrote:

mame (Yusuke Endoh) wrote:

headius (Charles Nutter) wrote:

One use:

my_class = class Foo
  ...
end

We can get the reference to a class being created immediately without adding "self" at the end.

I fail to see why it needs to be a local variable.
Why don't you use Foo instead of my_class?

How about this:

meta = class << self; end

Use:

meta = self.singleton_class

It is very arguable if the new idiom should be encouraged.
Personally, I don't like to see such a code. Rather, I prefer:

class Foo
  def self.init
    @foo = Foo.new
  end
  self.init
end

or even:

class Foo
  @@foo = Foo.new
end

I respect your opinion, but I fail to see why your way is better than mine.

Your way is too easy to overlook ".init" because a method call is not usually expected there.

Ultimately, the biggest reason I think this should happen is because class Foo; def bar; end; end returning :bar in 2.1 makes very little sense.

IMHO, most of casual users do not care too much for a return value.

Users not caring about return value is not a good reason to return a nonsensical value.

"It makes no sense" is not a good reason to change anything.
Rather, it is consistent and compatible to just return the last value.
We need better reason to break compatibility, I think.

This is my personal opinion, of course.

--
Yusuke Endoh

Updated by shugo (Shugo Maeda) over 10 years ago

I did a quick hack to try this proposal and found that some test failed with it:

https://gist.github.com/shugo/6739085

For example, bootstraptest/test_block.rb uses the last value of a class definition as follows:

assert_equal 'ok', %q{
  STDERR.reopen(STDOUT)
  class C
    define_method(:foo) do |&block|
      block.call if block
    end
    result = "ng"
    new.foo() {result = "ok"}
    result
  end
}

If class definitions are changed to return the defined class, there'll be no way to get values created in class definitions except using global variables or constants, etc.
I'm not sure whether there's any use case of the current behavior other than testing purposes, but please consider there might be some users of the current behavior.

Updated by headius (Charles Nutter) over 10 years ago

shugo (Shugo Maeda) wrote:

I did a quick hack to try this proposal and found that some test failed with it:

https://gist.github.com/shugo/6739085

For example, bootstraptest/test_block.rb uses the last value of a class definition as follows:

The case given is rather contrived; I have never seen code in the wild use last expression return from a class.

If class definitions are changed to return the defined class, there'll be no way to get values created in class definitions except using global variables or constants, etc.

This is a fair point, I suppose, but I still see more reasons to make the return value consistent than leave it as is and have classes suddenly returning a symbol for the last defined method. Most folks probably don't even know about the return value since if you're just doing "def" as last expression it has always been nil.

Updated by avdi (Avdi Grimm) over 10 years ago

On Sat, Sep 28, 2013 at 2:39 AM, shugo (Shugo Maeda)
wrote:

For example, bootstraptest/test_block.rb uses the last value of a class
definition as follows:

The only time I've ever used the return value of a class definition it's
been to get the class itself, by making self the last statement in the
class definition.

That said, if you really wanted to preserve the ability to return something
other from a class definition, could you make break <SOME_VALUE> override
the return, as it does in blocks?

--
Avdi Grimm
http://avdi.org

I only check email twice a day. to reach me sooner, go to
http://awayfind.com/avdi

Updated by headius (Charles Nutter) over 10 years ago

avdi (Avdi Grimm) wrote:

That said, if you really wanted to preserve the ability to return something
other from a class definition, could you make break <SOME_VALUE> override
the return, as it does in blocks?

That wouldn't be backward-compatible with anyone expecting last expression, but it's an excellent idea to address Shugo's concern.

Updated by jballanc (Joshua Ballanco) over 10 years ago

Just to throw my 2¢ in...

I think the main benefit to returning a symbol from def is that it enables the use of method decorators. Similarly, I would be in favor of returning the class defined from class so that we could also build class decorators. A trivial example:

def enumerable(klass)
  klass.send(:include, Enumerable)
  klass
end

enumerable
class Foo
  #...
end

Updated by headius (Charles Nutter) over 10 years ago

jballanc (Joshua Ballanco) wrote:

Just to throw my 2¢ in...

I think the main benefit to returning a symbol from def is that it enables the use of method decorators. Similarly, I would be in favor of returning the class defined from class so that we could also build class decorators. A trivial example:

def enumerable(klass)
  klass.send(:include, Enumerable)
  klass
end

enumerable
class Foo
  #...
end

Clever! Though I don't think that would parse the way you want. This would work though, obviously:

  enumerable class Foo ...

Updated by alexeymuranov (Alexey Muranov) almost 10 years ago

+1. I do not want to share my silly code where i would use it, but currently it may look something like that:

def MyClass
  # ...
  self
end.define_more_methods(x, y, z).freeze
Actions #23

Updated by sawa (Tsuyoshi Sawada) almost 9 years ago

I think this proposal would be useful in cases like this:

class Foo
  prepend module Bar
    def baz
      ...
    end
  end
end

Updated by cesario (Franck Verrot) almost 9 years ago

Shugo Maeda wrote:

If class definitions are changed to return the defined class, there'll be no way to get values created in class definitions except using global variables or constants, etc.
I'm not sure whether there's any use case of the current behavior other than testing purposes, but please consider there might be some users of the current behavior.

AFAICT, only one value (arguably it can be a complex object but I've never seen that kind of trick before) can be returned, so you'd still have to use globals/constants anyway. Am I mistaking here?

On a general level, it is surprising that class ...; end and Class.new don't return the same thing while def and define_method, something needs to be done to class to mimic this logic (even if I don't understand why we don't return a Method object today, like Rubinius does). Teaching Ruby, I'm saying class and Class.new are equivalent, but they're not with regards to this.

As a side-note, I'd like to present another use case. I've built a quick gem that loads Ruby code with rb_eval_string_protect and make this possible:

# foo.rb
require 'rb_import'
module Foo
  Bar = import("./my_class.rb")
end

f = import("./my_class.rb")

puts f.new.say_hello == Foo::Bar.new.say_hello # => true
puts f == Foo::Bar # => false

# my_class.rb
Class.new do
  def say_hello
    puts "Hello from my_class.rb"
  end
end

But in my_class.rb, I can't simply use a regular class definition "class Foo", I either got to use one of those

Class.new {}

class Foo
  ...
  self
end

class Foo
  ...
end
Foo
Actions #25

Updated by nobu (Nobuyoshi Nakada) about 8 years ago

  • Related to Feature #11905: Change the 'class' keyword to return a symbol added

Updated by yuki24 (Yuki Nishijima) about 8 years ago

I find myself repeating writing something like below (which, of course, doesn't work at this moment):

private_constant class Person
  ...
end

Although it would need to return a symbol, it would be great if we could do the above.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0