--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--

   Object oriented programming seems to  be the hottest  thing in these last
   years. Maybe it is because  people finally figure C++ code is more usable
   when you order you code,  instead of  hack it everywhere or maybe because
   a lot of  people  are trying  to make  money  out of Java,  and have thus 
   created a  hype around  Java and one of its strongest points,  namely the
   fact that it is an pure object oriented programming language. Be worried!
    
--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--

    So, we Euphorians, choose to OOP a bit in Euphoria. However all the 
    discussions that have been taking place about the way we should do that
    only prove that Euphoria is way above OO and is looking down at it. So
    lets get one thing strait. OO is not a have-to. There are circumstances
    where OO is the best choice but also many where it is not. Euphoria
    is off course always the best choice, because it leaves the choice up to
    you and your imagination. In Euphoria you have algoritmic freedom, but
    not expressive freedom. Which sort of means you can't hack, but you
    really don't need to anyway. 
    
--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--

    OOP means nothing more, than dividing your code based upon data types.
    We have a routine to display a window in any circumstance, but in OOP we
    order routines upon the data type, the subject. Routines that manipulate
    the windows are thus routines of a window. Not only do we order code
    that way, we also base our scope rules on them. All window routines are
    local to all window routines. And like there are routines there are also
    variables declared local to an object. Because of this, we can have 
    several instances of an object. Each with their own variables, that are
    independently. But we don't want all variables to be independent.
    Euphoria works much the same way, having local variables that are 
    static. However, we are not able to have a set of variables that are 
    independent of an object. We order routines per file. So, following a 
    few rules you could program OO, but your code would not look nice nor 
    would you maintain the same safety level. Types don't work on elements 
    of a sequence. Not to mention the extreme and unreadable amount of code 
    you must produce. You want to program in an OO way, not program a 
    complete OO preproccesor. Well, in Euphoria everything is possible, and
    so is this.
    
--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--

    However OO does not mean you have to recode or even reasign every
    attribute of your object. You can inherit attributes from other objects.
    Inheriting from more than one object though makes a spagetti-code, 
    because you loose all ordening. Have an instance of such an object as an 
    attribute is much cleaner. Using this 'oop.e' you can't even make an
    object yourself. You have to base it upon another, thereby inhereting
    its attributes. You can still make a new object by basing your new
    object on the EmptyClass. It is not really empty. It contains the three
    required attributes. A clone-method, a kill-procedure and a name-value.
    You can not have an object without these attributes, because there is no
    way to remove an attribute, once its added and the object is closed for
    development and open for use.
    
--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--
    
    You can have different types of attributes. With each type, also a
    function pointer is stored to verify the value of an attribute of such
    a type. This way, we have the same safety-level as in normal clean 
    Euphoria. There are already 4 types of attributes available:
    
        'Procedure' can contain a valid routine iD.
        'Method'    can contain a valid routine iD.
        'Function'  can contain a valid routine iD.
        'Value'     can contain any value.
        
    All these routines (only value contains something different) expect the
    first argument to contain the object's data sequence. Only routines 
    declared as 'Method' actually return that data sequence. Routines
    declared as 'Function' and 'Procedure' are thus unable to change the
    contents of the object's data sequence. Also be aware that you use
    value attributes for direct acces. All custom attribute types can be
    considered to be of the basic attribute type 'Value'. The names of
    the attributes are chosen upon their type and the object they belong
    to. Consider the three required attributes:
    
            MClone
            PKill
            VName
            
    Normall they would be preceeded by the class name in lower case, but 
    because these apply to all objects, I left them out. 
    
--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--

    -- Now, we are gonna make an object step by step:
    
    declare_class ("MyClass", NewClass)    
        
    -- We have now declared "MyClass" to be based upon the empty class, 
    -- logically (yeah, yeah ;-) named NewClass
    
    global constant
    
    -- These attributes are gonna be public
    
        MyAttribute = attrib (routine_id ("my_func"), Method)
        
    -- We have now added one attribute called: 'MyAttribute' with an
    -- (default) value of routine_id ("my_func"), the attribute type is 
    -- Method. (which contains a function pointer to verify the value)

    function pos_int (integer i)
        return i > 0
    end function
    constant PosInt = routine_id (pos_int)
    
    -- Now we have made an custom attribute called PosInt
    
    constant MyPrivateAttribute = attrib ( 5, PosInt )
    
    -- This attribute is private, because the constant is declared as local.
    -- It has a (default) value of 5 and is of the type PosInt.
    
    global constant NewMyClass = store_class ()
    
    -- We have now stored the class in MyClass, we can no longer add any
    -- attributes.
    -- We can't even change them, because we stored it in an constant.
    -- See it is now a superclass.
    -- You can use it to base other classes on. 
    
    declare_class ("MyOtherClass", NewMyClass)
    class MyOtherClass
    MyOtherclass = store_class ()
    
    -- We could have added some other attributes also.
    -- We could also change a few things:
    
    declare_class ("MyOtherClass", NewMyClass)
    assign_class (MyAttribute, routine_id ("my_other_func"))
    constant AnotherPrivateAttribute = attrib (3, PosInt)
    
    class MyOtherClass
    MyOtherClass = store_class ()
    constant NewMyOtherCLass = store_class ()
    
    -- We have now made NewMyOtherCLass which can be used to create instances
    -- in the future and we have created MyOtherClass which is not a constant
    -- and thus can be used.
    
--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--

    The next issue is safety. Well its simple now. Declare something as
    class and it gets checked against its own rules.
    
    In the above example, if I would have said:
    
    MyOtherClass[MyPrivateAttribute] = -4
    
    It will have given me a type check error and it would have told me an
    'unknown' type check error occured with the 5th element of the class that
    had named itself "MyOtherclass"
        
    If the custom attribute definition looked like this:
        
    function pos_int (integer i)
        if i <= 0 then
            error_message = "An positive integer is expected here!"
        end if
        return i > 0
    end function
    constant PosInt = routine_id (pos_int)

    It would have told use the same except it would not say the type check 
    error was unknown, but that is was "An positive integer is expected here"
    
    Another way to catch an error is to call: oop_trace (1)
    It will return TRUE instead of FALSE whenever type check error in a class
    occurs, but it will put tracing on.
    BTW.. Inside OOP.E is an "without trace" statement. So turn it on after 
    you include oop.e
    Example:
    
        include oop.e
        with trace
        
        oop_trace (1)   -- Turns tracing on after a type check error
    
    Easy huh ? ;-) Safety on top with Euphoria as seatbelt!
    
--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--

    But how do we addres these objects. You already guessed by now, but
    you want to be confirmed, don't ya ??!! (you right, I want to tell you 
    and blame you for that.. am I not a lamer or what ;)
    
    You could use the routines:
    
        return_value = function_class ( my_class, my_attrib, {my_arg1, ..} )
    
        my_class = method_class ( my_class, my_attrib, {my_arg1, .. } )
    
        procedure_class ( my_class, my_attrib, {my_arg1, .. } )            
    
        my_class = value_class ( my_class, my_attrib, my_new_value )
    
    But you could have done this also:
    
        return_value = call_func ( my_class[my_attrib], {my_class, my_arg1} )
    
        my_class = call_func ( my_class[my_attrib], {my_class, my_arg1} )

        call_proc ( my_class[my_attrib], {my_class, my_arg1} )    
        
        my_class[my_attrib] = my_new_value
        
--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--
    
    You can also see if objects relate to each other. Try this function:
    
       -1/0/1   = compare_class ( class_x, class_y )
        
    It will return 0 when the two classes are equal or different. Considering
    the attribute types, that is. The actual values are discarded. Compared
    are the function pointers of the attribute types. It will return -1 when
    all attributes of class_x can be found in the same order at the beginning
    of class_y. It will return a 1 instead of -1 when this is the other way
    around.
    
        boolean = equal_class ( class_x, class_y )
        
    Will return 1 when the attribute types of both classes are the same.
    Which does not mean they contain the same data!
    
    To check if two objects relate or not:
    
  if compare_class (class_x, class_y) or equal_class (class_x, class_y) then
        -- They relate!
  end if
  
    To kill an object:
    
        kill_class (class_x)
        
    From that point you may not use class_x anymore.
    This is helpfull with pointers to machine code or virtual screens, etc. 
    It can then be freed. Or maybe it has to write out some data, or close
    some connection. This always works, every object has the procedure kill,
    although its an empty routine by default.
    
        class_y = clone_class (class_x)
        
    Class_y is now like class_x .... an independent clone. Maybe it has its
    own file opened or memory block allocated, or something like that. This
    too always works.

--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--=o=--

    For more information, contact me:
    
    Ralf Nieuwenhuijsen
    nieuwen@xs4all.nl

