Re: Euphoria Objects
- Posted by David Cuny <dcuny at LANSET.COM> Dec 29, 2000
- 449 views
John wrote: > Creating objects with Euphoria? > I am kinda new with all this and > fuzzy about objects in general. You can think of an object as a collection of attributes about a thing. These can often be represented in a { object, attribute, value } tuple. For example, here's a representation of a cat: { MyCat, name, Mr. Fleabag } { MyCat, color, orange } { MyCat, attitude, snooty } { MyCat, prime directive, sleep } You get the general idea. You can have as many attributes for an object as you want. Implementing this is pretty simple: create a database of { object, attribute, value ) tuples: sequence db db = {} It's often convenient for objects have some sort of persistance, beyond the scope of the routine that created them. For this reason, you probably want to store your object in some global object, instead of a local variable. That way, you can pass the object's identifier to some routine, and the routine can permanantly alter the object, without having to pass it back to the caller. You need to be able to set, remove, and find tuples. Here's an example of what some of those routines might look like if you didn't care about efficiency: function findVal( object key, object attribute ) -- return index of object's attribute in database for i = 1 to length( db ) do if equal( db[i][1], key ) and equal( db[i][2], attribute ) then return i end if end for return 0 end function function getVal( object key, object attrib ) -- return the value of an object's attribute integer at at = findVal( key, attrib ) if at then return db[at][3] else return "unknown" end if end function procedure setVal( object key, object attrib, object val ) -- set or replace the value of an object's attribute integer at at = findVal( key, attrib ) if at then db[at][3] = val else db &= {{key, attrib, val}} end if end procedure Often, objects belong to classes, which are generalizations about a particular kind of thing. For example, "cat" could be a class: { cat, order, mammal } { cat, favorite food, tuna } I've declared that cats (in general) are all mammals, and have tuna as their favorite food. However, if my object declares something other than the class: { MyCat, favorite food, Fatty Deli Snax } then the object takes precedence. Classes can inherit from other classes. For example, "cat" could inherit from "mammal": { cat, class, mammal } { mammal, fur bearing, true } { mammal, milk producing, true } In this case, "mammal" would be the superclass of "cat". This could continue on and on, up the chain. At this point, it makes sense to rewrite the getVal() routine to implement inheritance: function getVal( object key, object attrib ) integer at at = findVal( key, attrib ) if at then return db[at][3] else -- recursively call the class at = findVal( key, "class" ) if at then return getVal( db[at][3], attrib ) else return "unknown" end if end if end function If this were more than a toy example, I wouldn't be using the object database to store class tuples. For one thing, there's too much chance for a name collision. But let's move on... Here's an example of the code in action: -- a convenience function procedure printVal( object key, object attrib ) puts( 1, getVal( key, attrib ) & '\n' ) end procedure -- class defaults setVal( "cat", "color", "orange" ) setVal( "cat", "favorite food", "tuna" ) setVal( "cat", "name", "Kitty" ) -- my cat setVal( "myCat", "class", "cat" ) setVal( "myCat", "name", "Mr. Fleabag" ) setVal( "myCat", "color", "brown" ) I can ask Euphoria what color my cat is: printVal( "myCat", "color" ) and Euphoria responds with "brown", which is part of the object's information. If I ask what his favorite food is: printVal( "myCat", "favorite food" ) Euphoria responds with "tuna", which was inherited from his class. If I ask for his age: printVal( "myCat", "age" ) Euphoria searches through the object, then the class, and superclasses (if there are any) and finally responds "unknown". Classes typically have "methods", which are simply a fancy name for routines. Methods can be implemented using by placing routine_id's in as values, and the calling something like this: procedure method( object key, object attrib. sequence args ) object val val = getVal( key, attrib ) if equal( val, "unknown" ) then puts( "unknown method " & attrib & "\n" ) else call_proc( val, args ) end if end procedure So you could create class methods like this: procedure dog_talk() puts( 1, "woof" ) end procedure setVal( "dog", "talk", routine_id("dog_talk") ) procedure cat_talk() puts( 1, "meow" ) end procedure setVal( "cat", "talk", routine_id("cat_talk") ) You can then call the method like this: method( "myCat", "talk", {} ) and Euphoria calls the correct method if it finds it among the object, class, or superclass. If you created a dog: addVal( "fido", "class", "dog" ) and called the same "talk" method: method( "fido", "talk", {} ) Euphoria responds "woof", calling with the dog method. This feature of the same routine name ("talk") having different behaviors depending on the class is known as "polymorphism". Objects, attributes, value; persistance; classes, superclasses, inheritance; methods and polymorphism... that just about covers it. I hope this helps! -- David Cuny