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 Keller 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,
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 reminiscent 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.

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

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.

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.

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.

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.

Setting the constant to nil will not effect existing instances.

···

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

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

Michael Keller 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(c) if methname.startswith('meth')]

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

            Michele Simionato

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

···

On 10/3/06, Michael Keller <ask@me.com> 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.

--
Tomasz Wegrzanowski [ http://t-a-w.blogspot.com/ ]

Logan Capaldo wrote:

···

On Tue, Oct 03, 2006 at 07:10:10PM +0900, Michael Keller 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.

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 06-10-05, at 00:39, Trans wrote:

Logan Capaldo wrote:

On Tue, Oct 03, 2006 at 07:10:10PM +0900, Michael Keller 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.

--
Jeremy Tregunna
Io Language Developer
jtregunna@blurgle.ca

Jeremy Tregunna 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.

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 05/10/06, Trans <transfire@gmail.com> wrote:

Jeremy Tregunna 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.")
  )

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 08:00, Trans wrote:

Jeremy Tregunna 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.")
  )

--
Jeremy Tregunna
jtregunna@blurgle.ca

Paul Battley 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.

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

···

On Thu, 5 Oct 2006, Jeremy Tregunna wrote:

  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.")
  )

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.)

--
in order to be effective truth must penetrate like an arrow - and that is
likely to hurt. -- wei wu wei

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 06-10-05, at 09:35, Trans wrote:

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"))

--
Jeremy Tregunna
jtregunna@blurgle.ca

  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.")
  )

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.)

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?

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 06-10-05, at 11:10, ara.t.howard@noaa.gov wrote:

On Thu, 5 Oct 2006, Jeremy Tregunna wrote:

--
Jeremy Tregunna
jtregunna@blurgle.ca

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

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.)

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 Fri, 6 Oct 2006, Jeremy Tregunna wrote:
--
in order to be effective truth must penetrate like an arrow - and that is
likely to hurt. -- wei wu wei

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

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.)

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 12:17, ara.t.howard@noaa.gov wrote:

On Fri, 6 Oct 2006, Jeremy Tregunna wrote:

--
Jeremy Tregunna
jtregunna@blurgle.ca

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 Fri, 6 Oct 2006, Jeremy Tregunna wrote:
--
in order to be effective truth must penetrate like an arrow - and that is
likely to hurt. -- wei wu wei

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 06-10-05, at 14:23, ara.t.howard@noaa.gov wrote:

On Fri, 6 Oct 2006, Jeremy Tregunna 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?

--
Jeremy Tregunna
jtregunna@blurgle.ca