In Ruby or Python can the very concept of Class be rewritten?
Asked Answered
D

5

9

first time at stack overflow.

I'm looking into using some of the metaprogramming features provided by Ruby or Python, but first I need to know the extent to which they will allow me to extend the language. The main thing I need to be able to do is to rewrite the concept of Class. This doesn't mean that I want to rewrite a specific class during run time, but rather I want to make my own conceptualization of what a Class is. To be a smidge more specific here, I want to make something that is like what people normally call a Class, but I want to follow an "open world" assumption. In the "closed world" of normal Classes, if I declare Poodle to be a subclass of Dog to be a subclass of Animal, then I know that Poodle is not going to also be a type of FurCoat. However, in an open world Class, then the Poodle object I've defined may or may not be and object of type FurCoat and we won't know for sure until I explain that I can wear the poodle. (Poor poodle.) This all has to do with a study I'm doing concerning OWL ontologies.

Just so you know, I've tried to find information online, but due to the overloading of terms here I haven't found anything helpful.

Super thanks, John

UPDATE: I just thought of a good use case for my open-world concept of Class. Perhaps this will provide a better understanding of what I really wish to do. I want to be able to "describe" a Class rather than define it. For instance, I want to be able to say that a Dog is anything that a) has four legs b) barks. Then I want to be able to create an object of unspecified Class, and describe that this object has four legs. At this point the object is still of unspecified type. Then I want to say that the object barks. At this point, the object will be known to be (possibly among other things) a Dog.

Devito answered 23/5, 2010 at 0:6 Comment(2)
Might it be multiple inheritance you're after? That would allow Poodle to be a subclass of both Dog and FurCoat, and as a consequence, finding that Poodle is a subclass of Dog doesn't exclude the possibility that it is a subclass of FurCoat.Rie
It seems that you're too fixated on this idea of modifying the class. Changing how is_a? works in Ruby to compare fields rather than the class types is possible and should do what you want.Stacee
J
7

I agree with Samir that it just sounds like duck typing. You don't need to care what 'type' an object really 'is' you only need bother with what an object can 'do'. This is true in both Ruby and Python.

However if you really are checking the types of classes and you really do need to have a Poodle object optionally also be a FurCoat at runtime, then the way to do this in Ruby is to mixin a FurCoat module into the Poodle object, as follows:

class Poodle; end
module FurCoat; def wear; end; end

my_poodle = Poodle.new
my_poodle.is_a?(Poodle) #=> true
my_poodle.is_a?(FurCoat) #=> false
my_poodle.wear #=> NoMethodError

# now we mix in the FurCoat module
my_poodle.extend(FurCoat)

# my_poodle is now also a FurCoat
my_poodle.is_a?(Poodle) #=> true (still)
my_poodle.is_a?(FurCoat) #=> true
my_poodle.wear #=> the wear method now works

EDIT (due to your updated question):

You still do not need to rewrite Class to achieve what you want, you just need to monkey-patch the kind_of? and is_a? (and potentially instance_of?) methods on Ruby's Kernel module. Since Ruby has open classes this is easily done:

class Module
    def obj_implements_interface?(obj)
        false
    end
end

module Kernel
    alias_method :orig_is_a?, :is_a?

    def is_a?(klass)
        orig_is_a?(klass) || klass.obj_implements_interface?(self)
    end
end

And then define for each class (or module) what it means for an object to implement its interface:

class Dog
    def self.obj_implements_interface?(obj)
        obj.respond_to?(:bark) && obj.respond_to?(:num_legs) && obj.num_legs == 4
    end
end

module FurCoat
    def self.obj_implements_interface?(obj)
        obj.respond_to?(:wear)
    end
end

Now test it:

my_poodle = Poodle.new
my_poodle.is_a?(FurCoat) #=> false

# now define a wear method on my_poodle
def my_poodle.wear; end
my_poodle.is_a?(FurCoat) #=> true
Jeminah answered 23/5, 2010 at 0:42 Comment(2)
First things first, I'm VERY impressed with stackOverflow and with you guys. I go off to get a bite and I come back and I have 63 views and some pretty intelligible answers to boot! So Samir and banister, you guys are getting closer to the functionality I need, but I don't think you can make it all the way there. I can't do duck typing because if it can fly, it isn;t necessarily a plane. The actual type is important, it's just incompletely known. I can't do mixin because I will sometimes need to infer that it's also a FurCoat without specifying so.I really need to be able to rewrite ClassDevito
Very cool. That does indeed get me closer to the result that I require. Although I really do wish that I had to freedom to create my own entity which mimics the Class entity. It seems, however, that a consensus is growing against that possibility.Devito
S
8

Sounds like duck typing to me. Just declare the methods you want and remember that it's easier to ask forgiveness than permission:

try:
    poodle.wear()
except (AttributeError, TypeError):
    pass
Stacee answered 23/5, 2010 at 0:16 Comment(0)
J
7

I agree with Samir that it just sounds like duck typing. You don't need to care what 'type' an object really 'is' you only need bother with what an object can 'do'. This is true in both Ruby and Python.

However if you really are checking the types of classes and you really do need to have a Poodle object optionally also be a FurCoat at runtime, then the way to do this in Ruby is to mixin a FurCoat module into the Poodle object, as follows:

class Poodle; end
module FurCoat; def wear; end; end

my_poodle = Poodle.new
my_poodle.is_a?(Poodle) #=> true
my_poodle.is_a?(FurCoat) #=> false
my_poodle.wear #=> NoMethodError

# now we mix in the FurCoat module
my_poodle.extend(FurCoat)

# my_poodle is now also a FurCoat
my_poodle.is_a?(Poodle) #=> true (still)
my_poodle.is_a?(FurCoat) #=> true
my_poodle.wear #=> the wear method now works

EDIT (due to your updated question):

You still do not need to rewrite Class to achieve what you want, you just need to monkey-patch the kind_of? and is_a? (and potentially instance_of?) methods on Ruby's Kernel module. Since Ruby has open classes this is easily done:

class Module
    def obj_implements_interface?(obj)
        false
    end
end

module Kernel
    alias_method :orig_is_a?, :is_a?

    def is_a?(klass)
        orig_is_a?(klass) || klass.obj_implements_interface?(self)
    end
end

And then define for each class (or module) what it means for an object to implement its interface:

class Dog
    def self.obj_implements_interface?(obj)
        obj.respond_to?(:bark) && obj.respond_to?(:num_legs) && obj.num_legs == 4
    end
end

module FurCoat
    def self.obj_implements_interface?(obj)
        obj.respond_to?(:wear)
    end
end

Now test it:

my_poodle = Poodle.new
my_poodle.is_a?(FurCoat) #=> false

# now define a wear method on my_poodle
def my_poodle.wear; end
my_poodle.is_a?(FurCoat) #=> true
Jeminah answered 23/5, 2010 at 0:42 Comment(2)
First things first, I'm VERY impressed with stackOverflow and with you guys. I go off to get a bite and I come back and I have 63 views and some pretty intelligible answers to boot! So Samir and banister, you guys are getting closer to the functionality I need, but I don't think you can make it all the way there. I can't do duck typing because if it can fly, it isn;t necessarily a plane. The actual type is important, it's just incompletely known. I can't do mixin because I will sometimes need to infer that it's also a FurCoat without specifying so.I really need to be able to rewrite ClassDevito
Very cool. That does indeed get me closer to the result that I require. Although I really do wish that I had to freedom to create my own entity which mimics the Class entity. It seems, however, that a consensus is growing against that possibility.Devito
L
6

No, you cannot do that in Ruby. In Ruby, the object model is baked into the language specification and is not accessible (and certainly not modifiable) from within the program. Even in Rubinius, which is a Ruby implementation written mostly in Ruby, and with amazing metaprogramming capabilities that extend far beyond what the Ruby specification offers, some of the fundamental primitives are hardwired in C++.

I am not that intimately familiar with Python, but I'm pretty sure it's the same way, even in PyPy.

You might be able to do that in Smalltalk, by modifying (or subclassing) the Behavior class, which is the superclass of Class and defines the behavior of both classes and metaclasses.

You can certainly do that in CLOS, or more precisely using CLOS's MOP (Meta-Object Protocol). After all, that's what a MOP is for: defining the object model.

The closest OO concept to what you are describing seems to be that of Predicate Classes. A predicate class is a class whose instances are not defined statically, but by a set of predicates: all objects that satisfy the set of predicates are instances of the class, as soon as and for as long as the predicate holds. In a language with mutable state, this obviously means that objects can "move" in and out of predicate classes as their state changes. It also means that at any given time an object can be an instance of many or no predicate classes.

The only mainstream language (for a rather broad definition of "mainstream") I know of that has predicate classes is Factor.

However, please note that even here, the predicates are defined and an object either fulfils them or it doesn't. There is no concept of discovering whether or not an object fulfils a predicate at runtime.

You might also be interested in Clojure's idea of ad-hoc taxonomy.

Last, but certainly not least, you might take a look at Mikel Evins's object system called Categories. The best description of Categories, is to simply follow the blog entries in chronological order:

  1. Protocols
  2. Categories
  3. A peek at Categories
  4. No Kings in Rome
  5. Up pops a reasonable facsimile thereof
  6. Different Categories of Categories
  7. Categories Bugs
  8. Flat Cat in a C3 Vat
  9. Categories 0.2
  10. Bard
  11. Bard intricacies

In the future, most of the development on Categories is going to be done in Mikel's new language Bard and you can follow their progress by following the Categories tag and the Bard tag on Mikel's new blog.

However, in general, I would say that the fact that Knowledge Management and Object-Orientation both use the word class is mainly a historic accident. I don't think that modeling one with the other is a good fit.

Lade answered 23/5, 2010 at 5:59 Comment(1)
Very good information Jörg. Thank you for drawing attention to the Predicate Class concept. It is very much like what I want, although with the one big exception that you already pointed out: in a Predicate Class if the object doesn't satisfy the predicate then it is known not to be in that class. I want to retain the third option which is "I don't know if it's in that class." So if I decide to push further I still will have to recreate a class like object. I'm still hoping for some way to do this in ruby. Rather than reprogramming Class, can I inherit from Module and make an OpenClass?Devito
K
2

In Python you can change the inheritence of a class at runtime, but at every given time a class is not a subclass of another one unless declared otherwise. There is no "may or may not" - that would need ternary logic which Python doesn't support. But of course you can write your own "Is-a" and "Has-a" functions to map OWL ontologies to Python classes.

Kentiga answered 23/5, 2010 at 0:26 Comment(0)
D
1

I think relying on a class structure, no matter how dynamic, is a step backwards when representing information with an open word assumption.

Classes serving as templates and objects serving as instances give absolutely no advantage when used with an OWA. Consider a Person class where we encode the knowledge that a person has 2 legs. However, we cannot deduce that a instance of Person will have two legs, as the person may have a disability.

If class properties don't mean anything as in the above example, there seems little point in using them or any other hierarchical structure to encode information.

Demonography answered 23/5, 2010 at 3:26 Comment(1)
I need to clarify here that, for now, I'm not concerned with whether or not my intentions are reasonable. Rather, I just want to know whether or not I can use Ruby to create a Class-like entity to replace the current Class entity. That being said, you do have some good points, and even as I created the "dog has 4 legs" example, I realized that it was overly simplistic. However I do have some other things in mind that still make me very interested in creating an open-world Class.Devito

© 2022 - 2024 — McMap. All rights reserved.