Dynamically changing superclass/mixins

I have strong interest in highly dynamic languages, particularly
prototype-based languages - and am still looking for the language that I
have to “abuse least” to achieve what I want (for my attempt in Java,
see http://diamondmud.sourceforge.net/, particularly
http://diamondmud.sourceforge.net/architecture.html)

Ruby, apart from just being a great language, has two features that are
fantastic for me:

  • open classes: I can add and remove methods “at runtime”
  • mixins; this is highly reminiscient of prototype based languages
    anyway.

Now I have two questions:

  1. Can I change the superclass for an already defined class? That is,
    remove an existing relationship or add a new one? I haven’t been able to
    figure that out.

  2. Can I also remove modules that I included into a class? I have only
    found a very ugly way to do this: undefine the class (Klass = nil) and
    define it anew. I am not sure even, what the effect was on existing
    instances; if they adapted the new behaviour or not.

I am fairly new to Ruby, so please forgive fundamental
misunderstandings!

Thanks a lot,
Michael

Michael K. wrote:

Now I have two questions:

  1. Can I change the superclass for an already defined class? That is,
    remove an existing relationship or add a new one? I haven’t been able to
    figure that out.

Not going to happen. But you can use modules and delegation to get a
similar effect. Subclassing is over rated anyway.

  1. Can I also remove modules that I included into a class? I have only
    found a very ugly way to do this: undefine the class (Klass = nil) and
    define it anew. I am not sure even, what the effect was on existing
    instances; if they adapted the new behaviour or not.

Here again, not happening, but this one might not be so far out of
reach “one day”. For now at least you have use other techniques.

In both cases, there’s probably a good alternative to accomplish what
you trying to do.

T.

On Tue, Oct 03, 2006 at 07:10:10PM +0900, Michael K. wrote:

I have strong interest in highly dynamic languages, particularly
prototype-based languages - and am still looking for the language that I
have to “abuse least” to achieve what I want (for my attempt in Java,
This may be considered blashpemous, but have you seen Io?
(http://www.iolanguage.com) It lets you alter the prototypes for an
object dynamic, which effectively gives you the abilities of removing
mixins and changing the superclass.

  1. Can I change the superclass for an already defined class? That is,
    remove an existing relationship or add a new one? I haven’t been able to
    figure that out.

  2. Can I also remove modules that I included into a class? I have only
    found a very ugly way to do this: undefine the class (Klass = nil) and
    define it anew. I am not sure even, what the effect was on existing
    instances; if they adapted the new behaviour or not.

Setting the constant to nil will not effect existing instances.

Logan C. wrote:

On Tue, Oct 03, 2006 at 07:10:10PM +0900, Michael K. wrote:

I have strong interest in highly dynamic languages, particularly
prototype-based languages - and am still looking for the language that I
have to “abuse least” to achieve what I want (for my attempt in Java,
This may be considered blashpemous, but have you seen Io?
(http://www.iolanguage.com) It lets you alter the prototypes for an
object dynamic, which effectively gives you the abilities of removing
mixins and changing the superclass.

Io does look interesting but I for one couldn;t help but shy aways b/c
of it’s lack of a block notation. Passing lambdas as regulat prameters
can get pretty rough on the old eyes.

T.

Jeremy T. wrote:

We don’t need block notation, everywhere you’d use blocks in Ruby, we
just use message objects. They’re cheap, have the same benefits, and
tie well into Io’s conceptual unification.

Which is why you write a for loop like this?

for(i, 99, 1,
writeln(i, " of beer on the wall, “, i, " of beer,”)
writeln(“take one down, pass it around,”)
writeln(bottle(i - 1), " of beer on the wall.")
)

T.

On 06-10-05, at 00:39, Trans wrote:

object dynamic, which effectively gives you the abilities of removing
mixins and changing the superclass.

Io does look interesting but I for one couldn;t help but shy aways b/c
of it’s lack of a block notation. Passing lambdas as regulat prameters
can get pretty rough on the old eyes.

We don’t need block notation, everywhere you’d use blocks in Ruby, we
just use message objects. They’re cheap, have the same benefits, and
tie well into Io’s conceptual unification.

On 05/10/06, Trans [email protected] wrote:

   writeln(i, " of beer on the wall, ", i, " of beer,")
   writeln("take one down, pass it around,")
   writeln(bottle(i - 1), " of beer on the wall.")

)

Clippy says, ‘It looks like you’re being sarcastic!’ (Correct me if I’m
wrong.)

It doesn’t look much like Ruby, granted, but it does tie in with the
rest of Io. Consider if/else:

if(cond, writeln(“aye”), writeln(“nay”))

Being able to pass multiple ‘blocks’ to a method facilitates things
like the above, which aren’t possible with Ruby’s single-block
approach.

Io’s an interesting language.

Paul.

On 06-10-05, at 08:00, Trans wrote:

   writeln(i, " of beer on the wall, ", i, " of beer,")
   writeln("take one down, pass it around,")
   writeln(bottle(i - 1), " of beer on the wall.")

)

Well yes. But only three of those arguments are evaluated (99, 1, and
the body of the loop). “i” is never evaluated, we just inspect the
name supplied by the user, and use it to set a slot which you can use
in the body which represents the element you’re looping over. 99 and
1 have a cached result set so they’re nice and quick. The cost of a
method/block to activate in Io is roughly 4x to 20x greater than for
a message to evaluate, which is why we tend to use messages instead
of blocks. (Just as a side note, I generally avoid using the for loop
and opt for Ranges myself, they just read better: 99 to(1) foreach
(i, …); but the same principal applies.)

That said, you could extend Io’s syntax without even having to get
involved with the parser to support ruby style block notation. I have
an example of manipulating Io’s AST to create a small subset of C by
doing nothing but manipulating the parse tree[1]. Not the best way to
go about doing it, but certainly evil and clever.

[1] - http://blurgle.blogspot.com/2006/09/fun-with-c-dsl.html

On 06-10-05, at 09:35, Trans wrote:

I agree. Multiple blocks are nice, but it could be be done with a
block
notation too if one wanted:

if(cond) {writeln(“aye”)} {writeln(“nay”)}

Hmm… does Io support currying? Then:

if(cond)(writeln(“aye”))(writeln(“nay”))

No, Io doesn’t support currying out of the box; we just don’t empose
an arity field on our methods/blocks. That is to say, you could
define a method which takes 3 arguments, if you only supply 2, the
last argument will receive “nil” as its value instead of a message
expression.

However, many attempts have been made (successfully I might add) to
add different levels of currying to Io. Asking on our mailing list is
likely to get you one or two examples =]

On Thu, 5 Oct 2006, Jeremy T. wrote:

set so they’re nice and quick. The cost of a method/block to activate in Io
is roughly 4x to 20x greater than for a message to evaluate, which is why we
tend to use messages instead of blocks. (Just as a side note, I generally
avoid using the for loop and opt for Ranges myself, they just read better:
99 to(1) foreach(i, …); but the same principal applies.)

jeremy-

can you elaborate on what messages are? where are the messages in this
code
snipper - or are there? alternatively can you just point me to a good
rtfm
link?

regards.

-a

Paul B. wrote:

Clippy says, ‘It looks like you’re being sarcastic!’ (Correct me if I’m wrong.)

A tinge perhaps, but not meant in a mean way. Just pointing out what I
meant by the lack of blocks.

It doesn’t look much like Ruby, granted, but it does tie in with the
rest of Io. Consider if/else:

if(cond, writeln(“aye”), writeln(“nay”))

Being able to pass multiple ‘blocks’ to a method facilitates things
like the above, which aren’t possible with Ruby’s single-block
approach.

I agree. Multiple blocks are nice, but it could be be done with a block
notation too if one wanted:

if(cond) {writeln(“aye”)} {writeln(“nay”)}

Hmm… does Io support currying? Then:

if(cond)(writeln(“aye”))(writeln(“nay”))

wouldn’t be so bad actually.

Io’s an interesting language.

I can agree. I’m just very asthetically guided with my initial contact
with things. Some of the code examples had me feeling a bit “lispy”.
Camelcase too is something I’m no longer fond. I guess maybe I’ve just
gotten very used to the over all look of Ruby.

But I argee, Io’s a tempting lanugage.

T.

On Fri, 6 Oct 2006, Jeremy T. wrote:

one last comment, and then i’ll move over to the io list, since this is
really
ot (sorry everyone)…

(Apologies if your mail client made that look funny.)

would the above be read something like

m = method a, a * a

m 5

otherStuff

??

is the parse non-ambiguous as

(method a, a * a) call 5 ; otherStuff

??

i’m skimming docs now. it looks very interesting. the only quirk
IsForNonNativeEnglishSpeakers ;-(

cheers.

-a

On 06-10-05, at 12:17, [email protected] wrote:

because I

would the above be read something like

m = method a, a * a

m 5

otherStuff

It could be yes. Though the only reason I didn’t assign the method to
a slot (variable) beforehand was because assignment in Io is
different. The parser translates a := b into setSlot(“a”, b) so I
didn’t want to confuse any more than was bound to happen by
introducing this oddity.

is the parse non-ambiguous as

(method a, a * a) call 5 ; otherStuff

I’m not exactly sure what you’re after here. The snippit of code in
question isn’t ambiguous.

On 06-10-05, at 11:10, [email protected] wrote:

body of the loop). “i” is never evaluated, we just inspect the
generally
link?
Sure. It’s really simple actually. Every bit of Io code you see
(excluding comments) are messages. Io is a message based language,
there are no keywords, and infact, the only reason comments aren’t
messages is because I havn’t completed my patch to Io’s parser and
lexer to transform #, // and /* */ into an actual message yet.

Considering the parse tree for the above can be a little more verbose
than it needs to to demonstrate how Io is parsed, I’ll draw out the
parse tree as a list of lists (try not to think of it as a tree
because it’s not really). The code example is:

method(a, a * a) call(5); otherStuff

The above is transformed into several messages

— method — call
| a 5
| a - *
| a
|- otherStuff

(Apologies if your mail client made that look funny.)

The horizontal list (delimited by —'s) can be seen as the
“attached” tree. This tree represents messages attached to one
another. Using a ruby example: foo.bar <-- “bar” is said to be
“attached” to foo. The vertical tree stemming down from “method” and
ending in |- represents the “next” message. Again using a ruby
example: foo; bar <-- “bar” is said to be “foo”'s next message. The
tree of messages to the right of the bar’s dropping down from method
is the list of arguments of a message and all the same attached/next
rules apply within argument lists (they’re afterall, just the start
of new nested message trees).

A quick note on how “*” (and all other operators are parsed); You do
not require explicit parenthesis to use them (much like Ruby);
however, not all methods work this way. The parser sees an operator
character and turns it into a proper method call. That is, the above
code “a * a” is seen at parse time and transformed into the code: a *
(a). You can write code like that if you want, but it’s not generally
done outside of the VM. That is the one real oddity.

In the above way, it’s possible to easily conceptualize Io code in
terms of how it’s parsed. However, we also expose the parse tree in
code and give you primitives for manipulating it, so Io code can be
difficult to read based on this (I can already hear Haskell people
plugging their ears and singing “lalalala, I can’t hear you”) just
because it’s near impossible to reason about an Io program without
the entire program. You cannot be sure that a component in one object
will be handled how it’s parsed. That said, it’s not really as bad as
I make it out to be; most code can easily be reasoned about.

Sorry for the quick and loose description, I intend on dedicating
lots of time in writing documentation which covers messages, how
they’re used, what you can do with them, etc., but Io currently has a
chronic lack of in-depth documentation. We’re still in a late beta
stage at the moment, but the language is pretty much solidified (some
work on the standard lib still to be done), but the goal is to have
decent documentation readily available within the next year. So my
apologies for the sparse information on the website.

On Fri, 6 Oct 2006, Jeremy T. wrote:

It could be yes. Though the only reason I didn’t assign the method to a slot
(variable) beforehand was because assignment in Io is different. The parser
translates a := b into setSlot(“a”, b) so I didn’t want to confuse any more
than was bound to happen by introducing this oddity.

right. i just read about := != = …

is the parse non-ambiguous as

(method a, a * a) call 5 ; otherStuff

I’m not exactly sure what you’re after here. The snippit of code in question
isn’t ambiguous.

i meant, how many parens can you omit?

-a

On 06-10-05, at 14:23, [email protected] wrote:

On Fri, 6 Oct 2006, Jeremy T. wrote:

is the parse non-ambiguous as
(method a, a * a) call 5 ; otherStuff

I’m not exactly sure what you’re after here. The snippit of code
in question isn’t ambiguous.

i meant, how many parens can you omit?

You can only omit parens for operators. However, you can dynamically
add operators at runtime. Such as:

Message OperatorTable rightOperators atPut(“call”, 4) // gives “call”
a precedence of 4

Then you could write:

method(a, a * a) call 5; doStuff

and Io parses it as such:

method(a, a *(a)) call(5); doStuff

However, this isn’t generally recommended as it then brings operator
precedence into the picture and you may be a little bit surprised as
to how things get parsed then. I.e., if you wanted to calculate the
value of 1 + 5 and then the factorial of that result, and you write:

1 + 5 factorial

You’d be in for a shock as that would be parsed to 1 +(5 factorial).
You’d need to either explicitly group 1 + 5 such as (1 + 5) or give
the parens explicitly to addition such as: 1 +(5). I personally don’t
like operator precedence, it only complicates things, but that’s a
bikeshed for another list. =D

On 10/3/06, Michael K. [email protected] wrote:

Now I have two questions:

  1. Can I change the superclass for an already defined class? That is,
    remove an existing relationship or add a new one? I haven’t been able to
    figure that out.

  2. Can I also remove modules that I included into a class? I have only
    found a very ugly way to do this: undefine the class (Klass = nil) and
    define it anew. I am not sure even, what the effect was on existing
    instances; if they adapted the new behaviour or not.

Indeed you can, using evil.rb

For the first question:

require ‘evil.rb’

class Foo
def hello
puts “I’m foo”
end
end

class Bar
def hello
puts “I’m bar”
end
end

class Xyzzy < Foo
end

x = Xyzzy.new
x.hello # => I’m foo
Xyzzy.superclass = Bar # From evil.rb
x.hello # => I’m bar

For the second:
require ‘evil.rb’

class Object
def hello
puts “I’m Object”
end
end

module X
def hello
puts “I’m X”
end
end

class Y
include X
end

y = Y.new
y.hello # => I’m X
Y.superclass = Y.superclass # Changing superclass resets all modules
y.hello # => I’m Object

evil.rb is not officially supported of course, use at own risk, blah
blah

Michael K. wrote:

  1. Can I change the superclass for an already defined class? That is,
    remove an existing relationship or add a new one? I haven’t been able to
    figure that out.

  2. Can I also remove modules that I included into a class? I have only
    found a very ugly way to do this: undefine the class (Klass = nil) and
    define it anew. I am not sure even, what the effect was on existing
    instances; if they adapted the new behaviour or not.

Since people mentioned Io, I feel free to post a Python solution:

class Base(object):
def meth(self):
print ‘called B.meth’

class Mixin1(object):
def meth1(self):
print ‘called meth1’

class Mixin2(object):
def meth2(self):
print ‘called meth2’

class C(Base, Mixin1):
pass

c = C()

c.meth()
c.meth1()

C.bases = (Base, Mixin2) # change the base classes (ick!)

print [methname for methname in dir© if methname.startswith(‘meth’)]

c.meth()
c.meth2()
c.meth1() # this gives an error now

        Michele Simionato

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs