> < ^ Date: Thu, 17 Sep 1998 15:38:03 +0200 (CEST)
> < ^ From: Thomas Breuer <Thomas.Breuer@Math.RWTH-Aachen.DE >
> < ^ Subject: Re: Overloading Operations in GAP4

Dear GAP Forum,

Jacob Hirbawi wrote

I am having trouble figuring out how overloading operations is done in
GAP4.
The GAP3 equivalent of what I'm tryuing to do is :

MyOps:=rec();
MyOps.\+:=function(a,b) return rec(x:=a.x+b.x+7,operations:=MyOps);end;
A:=rec(x:=3,operations:=MyOps);
B:=rec(x:=5,operations:=MyOps);

so if I then type A+B I would get rec(x:=15,operations:=MyOps);

This problem is one that can be solved with the
``GAP 3 compatibility mode''
(see Chapter ``Migrating to GAP 4'' in the GAP 4 Tutorial).
Operations records are supported in GAP 4, to a certain extent,
after the library file `compat3c.g' has been read;
note that this file is not read automatically when GAP 4 is started.
Operations records must be created with the function `OperationsRecord'
(this was usual also in GAP 3),
so starting with an empty record will not work.
For your intended application,
you should thus start with the following two lines of code.

gap> ReadLib( "compat3c.g" );
gap> MyOps:= OperationsRecord( "MyOps" );;
HasMyOps := NewProperty( "HasMyOps", IsObject );

The line printed by GAP shows how the simulation of GAP 3
operations records is dealt with in GAP 4.
Now you add your addition function to the operations record,
and again GAP 4 prints a translation to GAP 4 code.

gap> MyOps.\+:= function(a,b)
> return rec(x:=a.x+b.x+7,operations:=MyOps);
> end;;
# If the following method installation matches the requirements
# of the operation `SUM' then `InstallMethod' should be used.
# It might be useful to replace the rank `SUM_FLAGS' by `0'.
InstallOtherMethod( SUM,
"for object with `MyOps' as first argument",
true,
[ HasMyOps, IsObject ], SUM_FLAGS,
MyOps.\+ );

# For binary infix operators, a second method is installed
# for the case that the object with `MyOps' is the right operand;
# since this case has higher priority on GAP 3, the method is
# installed with higher rank `SUM_FLAGS + 1'.
InstallOtherMethod( SUM,
"for object with `MyOps' as second argument",
true,
[ IsObject, HasMyOps ], SUM_FLAGS + 1,
MyOps.\+ );

Now your example does in fact work.
(In more complicated cases, one might run into problems,
but this was already the case in GAP 3.
For example, suppose you want to support the addition of two
operands having different operations records;
then it is not clear which of the two addition functions
is to be chosen.)

gap> A:=rec(x:=3,operations:=MyOps);
rec( x := 3, operations := MyOps )
gap> B:=rec(x:=5,operations:=MyOps);
rec( x := 5, operations := MyOps )
gap> A + B;
rec( x := 15, operations := MyOps )

So far, so good.
If you are now interested to translate your code to GAP 4
in the sense that no ``compatibility mode'' is needed,
you can use what GAP 4 printed instead of your input.

HasMyOps := NewProperty( "HasMyOps", IsObject );

This command creates a new property with name `HasMyOps',
which is applicable to any GAP object
(hence the `IsObject' argument).
The idea is that the addition is installed only for
objects that ``have this property'',
so we need a way to create objects with this property;
such objects cannot be records,
but they may behave similarly to records.
A function creating such an object,
with component `x' bound and with the property,
may look as follows.

MyType := NewType( NewFamily( "MyFam" ), HasMyOps );

MyObject := function( val )
    return Objectify( MyType, rec( x:= val ) );
end;

The addition function shall return an object with the
property, so we change it as follows.

mysum := function( a, b )
    return MyObject( a!.x + b!.x + 7 );
end;;

Note that the component access for these objects
works via `!.' instead of `.';
further note that no operations record needs to appear here,
the property takes its role.

Finally, we install the addition for at least one argument
with the new property, as had been printed by GAP 4 in the
session shown above.

InstallOtherMethod( SUM,
"for object with `MyOps' as first argument",
true,
[ HasMyOps, IsObject ], 0,
mysum );

InstallOtherMethod( SUM,
"for object with `MyOps' as second argument",
true,
[ IsObject, HasMyOps ], 1,
mysum );

And now the example works (again).

gap> A:= MyObject( 3 );
<object>
gap> B:= MyObject( 5 );
<object>
gap> A + B;
<object>
gap> last!.x;
15

We may install a method to print our objects in a nice way;
We could have done this for the operations record `MyOps'
in the compatibility mode,
the printed output would look similar to the following.

InstallOtherMethod( PRINT_OBJ,
    "for object with `MyOps' as first argument",
    true,
    [ HasMyOps ], 0,
    function( obj ) Print( "MyObject( ", obj!.x, " )" ); end );

Now the example behaves as follows.

gap> A;  B;  A + B;
MyObject( 3 )
MyObject( 5 )
MyObject( 15 )

Maybe now we want to improve the installation.
The addition function we want to use is apparently
thought only for the case that *both* operands have
the property (and a component `x').
So it is reasonable to replace the two methods for
the addition by one method for which both arguments
are required to have the property.

InstallOtherMethod( SUM,
"for two objects with `MyOps'",
true,
[ HasMyOps, HasMyOps ], 0,
mysum );

At first sight, the GAP 4 approach seems to be much more
complicated.
But the last example shows that in GAP 4, each methods can
be installed more specific for the appropriate situation.
Moreover, it is for example possible to install a method
for the addition of an integer and a `HasMyOps' object;
note that --contrary to the situation in GAP 3--
such a method is independent from already existing methods
in the sense that these need not be changed when
new functionality is added.

I hope this is of some help.
Thomas


> < [top]