views:

182

answers:

3

Sometimes I need such method, which could change class of its own object. There are String#delete!, #downcase!, #encode!, #gsub!, #strip!, #slice!, etc. They are all trying to change string, but resulting class is anyway still String. And I want a method, which can convert String to Array. Some way to make this:

irb(main):082:0> str = "qwerty"
=> "qwerty"
irb(main):083:0> str.split! "e"
=> ["qw", "rty"]
irb(main):084:0> str
=> ["qw", "rty"]

Is it possible? Maybe some cool japan kung-fu or ugly bicycles — I would like to see any solution.

+10  A: 

Nope, not possible. Objects can't change their classes in Ruby.

In Smalltalk, for example, you could use become::

becomeSubStrings: aString
    self become: (self subStrings: aString).

If you call it like this:

s := 'qwerty'.
s becomeSubStrings: 'e'.

Now, s is an array:

Transcript show: s printString.

The result is:

#('qw' 'rty')

Technically, become: doesn't change the class of the object, rather it lets one object become another object. In this case, we let self become the result of self subStrings:, which splits the string into an array of substrings. The result is the same: the original string receiver is now an array.

Jörg W Mittag
Short and to the point. +1.
JUST MY correct OPINION
"I don't know, how to solve it" is not a garantee of impossibility, until it does not have more official proofs.
Nakilon
Nakilon: Since there is no official language specification for Ruby, you take the Ruby MRI as reference implementation. And the Ruby MRI doesn't support changing the class of an object.
joschi
@joschi: While Ruby is not specified in the way e.g. Common Lisp is, the various new implementations at least lead to RubySpec: http://www.rubyspec.org/
Michael Kohl
Are there any languages where objects can change their class?
Andrew Grimm
@Andrew Grimm: In Smalltalk, you could do it using `become:`.
Jörg W Mittag
@Andrew Grimm: And in Javascript, you can set an object's prototype, which is roughly equivalent (since it uses prototype-based inheritance rather than class-based inheritance).
Chuck
@Chuck: I'm pretty sure that setting the prototype property is not possible in ECMAScript/JavaScript. It *is* possible in ScriptMonkey/SpiderMonkey/TraceMonkey/JägerMonkey (i.e. in Firefox) and maybe also in Rhino, but that is a non-standard, interpreter-specific extension.
Jörg W Mittag
A: 

Jörg W Mittag's answer is solid. It is a fundamental attribute of Ruby as a language that you can't change an object's class.

The best you can do is re-assign the variable as I am sure you are already doing:

irb(main):082:0> str = "qwerty"
=> "qwerty"
irb(main):083:0> str = str.split "e"
=> ["qw", "rty"]
irb(main):084:0> str
=> ["qw", "rty"]
bjeanes
A: 

This is only possible using evil-ruby (at least I'm pretty sure evil-ruby supports this). It is a really clever hack that coaxes Ruby into doing things it was never meant to do, but also an unportable hack that has been known to cause actual segfaults in Ruby code using it. You should absolutely not use it in production code.

Otherwise, give your string an internal array and make it delegate all messages to the array (along the lines of BlankSlate + Forwardable).

It's still not something I would want to do. I can't imagine a situation where such a minor convenience outweighs the hackness of the implementation.

Chuck
This is similar to the Firefox thing: evil-ruby is a MRI extension. I.e.: it is *not* Ruby, it's C. And it works only on MRI, not on YARV, JRuby, IronRuby, Rubinius, MacRuby, SmallRuby, MagLev, Cardinal, tinyrb, RubyGoLightly, BlueRuby, HotRuby, Ruby Red, Red Sun, Ruby.NET, XRuby or any other Ruby implementation. Basically, what you are saying is: "if you patch the interpreter, so that the language it interprets is no longer Ruby, then you can do it."
Jörg W Mittag
And you are right about the stability: evil-ruby works by modifying private, internal, undocumented, unstable implementation details of MRI. Since those are not part of the public API, and often not even part of any API *at all* (public or private) but simply incidental implementation details, they can change in incompatible ways even in minor releases (and even patch releases) or through minor patches (e.g. Ruby Enterprise Edition), which if you're lucky will result in the interpreter in spectacular ways and if not, it will simply result in program unpredictably giving the wrong answers.
Jörg W Mittag