[Top] [Up] [Previous] [Next] [Index]

# 9 Migrating to GAP 4

### Sections

1. Changed Command Line Options
2. Fail
3. Changed Functionality
4. Changed Variable Names
5. Naming Conventions
6. Immutable Objects
7. Copy
8. Attributes vs. Record Components
9. Different Notions of Generation
10. Operations Records
11. Operations vs. Dispatcher Functions
12. Parents and Subgroups
13. Homomorphisms vs. General Mappings
14. Homomorphisms vs. Factor Structures
15. Isomorphisms vs. Isomorphic Structures
16. Elements of Finitely Presented Groups
17. Polynomials
18. The Info Mechanism
19. Debugging
20. Compatibility Mode

This chapter is intended to give users who have experience with GAP 3 some information about what has changed in GAP 4.

In particular, it informs about changed command line options (see Changed Command Line Options), the new global variable `fail` (see Fail), some functions that have changed their behaviour (see Changed Functionality) or their names (see Changed Variable Names), and some conventions used for variable names (see Naming Conventions).

Then the new concepts of GAP 4 are sketched, first that of mutability or immutability (see Immutability), with the explanation of related changes in functions that copy objects (see Copy), then the concepts of operations and method selection, which are compared with the use of operations records in GAP 3 (see Attributes vs. Record Components, Operations Records, and Operations vs. Dispatcher Functions).

More local changes affect the concepts of notions of generation (see Different Notions of Generation), of parents (see Parents and Subgroups), of homomorphisms (see Homomorphisms vs. General Mappings, Homomorphisms vs. Factor Structures, and Isomorphisms vs. Isomorphic Structures), how elements in finitely presented groups are treated (see Elements of Finitely Presented Groups), how information about progress of computations can be obtained (see The Info Mechanism), and how one gets information in a `break` loop (see Debugging).

While a ``GAP 3 compatibility mode'' is provided (see Compatibility Mode), its use will disable some of the new features of GAP 4. Also it certainly can only try to provide partial compatibility.

For a detailed explanation of the new features and concepts of GAP 4, see the manual ``Programming in GAP''.

## 9.1 Changed Command Line Options

In GAP 4, the `-l` option is used to specify the root directory (see GAP Root Directory in the Reference Manual) of the GAP distribution, which is the directory containing the `lib` and `doc` subdirectories. Note that in GAP 3 this option was used to specify the path to the `lib` directory.

The `-h` option of GAP 3 has been removed, the path(s) for the documentation are deduced automatically in GAP 4.

The option `-g` is now used to print information only about full garbage collections. The new option `-g -g` generates information about partial garbage collections too.

## 9.2 Fail

There is a new global variable

- `fail`

  in GAP 4. It is intended as a return value of a function for the case that it could not perform its task. For example, `Inverse` returns `fail` if it is called with a singular matrix, and `Position` returns `fail` if the second argument is not contained in the list given as first argument.

  GAP 3 handled such situations by either signalling an error, for example if it was asked for the inverse of a singular matrix, or by (mis)using `false` as return value, as in the example `Position`. Note that in the first example, in GAP 3 it was necessary to check the invertibility of a matrix before one could safely ask for its inverse, which meant that roughly the same work was done twice.

  ## 9.3 Changed Functionality

  Some functions that were already available in GAP 3 behave differently in GAP 4. This section lists them.

- `Orbit(` G`,`pnt`)`

  The GAP 3 manual promised that pnt would be the first entry of the resulting orbit. This was wrong already there in a few cases, therefore GAP 4 does not promise anything about the ordering of points in an orbit.

- `Order(` g`)`

  only takes the element g and computes its multiplicative order. Calling `Order` with two arguments is permitted only in the GAP 3 compatibility mode, see Compatibility Mode. (Note that it does not make sense anymore to specify a group as first argument w.r.t. which the order of the second argument shall be computed, see Elements of Finitely Presented Groups.)

- `Position(` list`,` obj`)`

  If obj is not contained in the list list then `fail` is returned in GAP 4 (see Fail), whereas `false` was returned in GAP 3.

- `PermGroupOps.ElementProperty(` G`,` prop`[,` K`] )`

  In GAP 3, this function took either two or three arguments, the optional argument K being a subgroup of G that stabilizes prop in the sense that for any element g in G, either all elements or no element in the coset ``g`*` K`` have the property prop.

  The GAP 4 function `ElementProperty`, however, takes between two and four arguments, and the subgroup K known from GAP 3 has to be entered as the fourth argument not the third. (The third argument in the GAP 4 function denotes a subgroup U stabilizing prop in the sense that either all elements or no element in right cosets ``U`*` g`` have the property prop.)

  (This discrepancy was discovered only in March 2002, short before the release of GAP 4.3.)

- `Print(` obj`, ... )`

  Objects may appear on the screen in a different way, depending on whether they are printed by the read eval print loop or by an explicit call of `Print`. The reason is that the read eval print loop calls the operation `ViewObj` and not `PrintObj`, whereas `Print` calls `PrintObj` for each of its arguments. This permits the installation of methods for printing objects in a short form in the read eval print loop while retaining `Print` to display the object completely. See also Section View and Print in the Reference Manual.

  (`PrintObj` is installed as standard method `ViewObj`, so it is not really necessary to have a `ViewObj` method for an object.)

- `PrintTo(` filename`,` obj`, ... )`

  In GAP 3, `PrintTo` could be (mis)used to ``redirect'' the text printed by a function (that is, not only the output of a function) to a file by entering the function call as second argument. This was used mainly in order to avoid many calls of `AppendTo`. In GAP 4, this feature has disappeared. One can use streams (see Chapter Streams in the Reference Manual) instead in order to write files efficiently.

  ## 9.4 Changed Variable Names

  Some functions have changed their name without changing the functionality. A -- probably incomplete -- list follows

          GAP 3                   GAP 4

          AgGroup                 PcGroup            # (also composita)
          ApplyFunc               CallFuncList
          Backtrace               Where
          CharTable               CharacterTable     # (also composita)
          Denominator             DenominatorRat
          DepthVector             PositionNot
          Elements                AsSSortedList
          IsBijection             IsBijective
          IsFunc                  IsFunction
          IsMat                   IsMatrix
          IsRec                   IsRecord
          IsSet                   IsSSortedList
          LengthWord              Length
          NOfCyc                  Conductor
          Numerator               NumeratorRat
          NormedVector            NormedRowVector
          Operation               Action            # (also composita)
          Order(G,g)              Order(g)
          OrderMat                Order
          OrderPerm               Order
          RandomInvertableMat     RandomInvertibleMat
          RecFields               RecNames
          X                       Indeterminate

  See Section Compatibility Mode for a way to make the old names available again.

  ## 9.5 Naming Conventions

  The way functions are named has been unified in GAP 4. This might help to memorize or even guess names of library functions.

  If a variable name consists of several words then the first letter of each word is capitalized.

  If the first part of the name of a function is a verb then the function may modify its argument(s) but does not return anything, for example `Append` appends the list given as second argument to the list given as first argument. Otherwise the function returns an object without changing the arguments, for example `Concatenation` returns the concatenation of the lists given as arguments.

  If the name of a function contains the word `By` then the return value is thought of as built in a certain way from the parts given as arguments. For example, `GroupByGenerators` returns a group built from its group generators, and creating a group as a factor group of a given group by a normal subgroup can be done by taking the image of `NaturalHomomorphismByNormalSubgroup` (see also Homomorphisms vs. Factor Structures). Other examples of ```By`'' functions are `GroupHomomorphismByImages` and `UnivariateLaurentPolynomialByCoefficients`.

  If the name of a function contains the word `Of` then the return value is thought of as information deduced from the arguments. Usually such functions are attributes (see Attributes in this Tutorial and Attributes in the Reference Manual). Examples are `GeneratorsOfGroup`, which returns a list of generators for the group entered as argument, or `DiagonalOfMat`.

  For the setter and tester functions of an attribute attr (see Attributes vs. Record Components in this Tutorial and Attributes in the Reference Manual) the names `Set` attr`` resp. `Has` attr`` are available.

  If the name of a function fun1 ends with `NC` then there is another function fun2 with the same name except that the `NC` is missing. `NC` stands for ``no check''. When fun2 is called then it checks whether its arguments are valid, and if so then it calls fun1. The functions `SubgroupNC` and `Subgroup` are a typical example.

  The idea is that the possibly time consuming check of the arguments can be omitted if one is sure that they are unnecessary. For example, if an algorithm produces generators of the derived subgroup of a group then it is guaranteed that they lie in the original group; `Subgroup` would check this, and `SubgroupNC` omits the check.

  Needless to say, all these rules are not followed slavishly, for example there is one operation `Zero` instead of two operations `ZeroOfElement` and `ZeroOfAdditiveGroup`.

  ## 9.6 Immutable Objects

  GAP 4 supports ``immutable'' objects. Such objects cannot be changed, attempting to do so issues an error. Typically attribute values are immutable, and also the results of those binary arithmetic operations where both arguments are immutable, see Section Vectors and Matrices. For example, `[ 1 .. 100 ] + [ 1 .. 100 ]` is a mutable list and `2 * Immutable( [ 1 .. 100 ] )` is an immutable list, both are equal to the (mutable) list `[ 2, 4 .. 200 ]`.

  There is no way to make an immutable object mutable, one can only get a mutable copy by `ShallowCopy`. The other way round, `MakeImmutable` makes a (mutable or immutable) object and all its subobjects immutable; one must be very careful to use `MakeImmutable` only for those objects that are really newly created, for such objects the advantage over `Immutable` is that no copy is made.

  More about immutability can be found in Sections Immutability in this tutorial and Mutability and Copyability in the Reference Manual.

  ## 9.7 Copy

  The function `Copy` of GAP 3 is not supported in GAP 4. This function was used to create a copy cop of its argument obj with the properties that cop and obj had no subobjects in common and that if two subobjects of obj were identical then also the corresponding subobjects of cop were identical.

  The possibility of having immutable objects (see Immutability) can and should be used to avoid unnecessary copying. Namely, given an immutable object one needs to copy it only if one wants to get a modified object, and in such a situation usually it is sufficient to use `ShallowCopy`, or at least one knows how deep one must copy in order to do the changes one has in mind.

  For example, suppose you have a matrix group, and you want to construct a list of matrices by modifying the group generators. This list of generators is immutable, so you call `ShallowCopy` to get a mutable list that contains the same matrices. If you only want to exchange some of them, or to append some other matrices, this shallow copy is already what you need. So suppose that you are interested in a list of matrices where some rows are also changed. For that, you call `ShallowCopy` for the matrices in question, and you get matrices whose rows can be changed. If you want to change single entries in some rows, `ShallowCopy` must be called to get mutable copies of these rows. Note that in all these situations there is no danger to change, i.e., to destroy the original generators of the matrix group.

  If one needs the facility of the `Copy` function of GAP 3 to get a copy with the same structure then one can use the new GAP 4 function `StructuralCopy`. It returns a structural copy that has no mutable subobject in common with its argument. So if `StructuralCopy` is called with an immutable object then this object itself is returned, and if `StructuralCopy` is called with a mutable list of immutable objects then a shallow copy of this list is returned.

  Note that `ShallowCopy` now is an operation. So if you create your own type of (copyable) objects then you must define what a shallow copy of these objects is, and install an appropriate method.

  ## 9.8 Attributes vs. Record Components

  In GAP 3, many complex objects were represented via records, for example all domains. Information about these objects was stored in components of these records. For the user, this was usually not relevant, since there were functions for computing information about the objects in question. For example, if one was interested in the size of a group then one could call `Size`.

  But since it was guaranteed that the size of a domain D was stored as value of the component `size`, it was allowed to access ``D`.size` if this component was bound, and a check for this was possible via `IsBound(` D`.size )`.

  In GAP 4, only the access via functions is admissible. One reason is the following basic rule.

  From the information that a given GAP 4 object is for example a domain, one cannot conclude that this object has a certain representation.

  For attributes like `Size`, GAP 4 provides two related functions, the setter and the tester of the attribute, which can be used to set an attribute value and to check whether the value of an attribute is already stored for an object (see also Attributes in the Reference Manual). For example, if D is a domain in GAP 4 then `HasSize(` D`)` is `true` if the size of D is already stored, and `false` otherwise. In the latter case, if you know that the size of D is size then you may store it by `SetSize(` D`,` size`)`.

  Besides the flexibility in the internal representation of objects, storing information only via function calls has also the advantage that GAP 4 is able to draw conclusions automatically. For example, as soon as it is stored that a group is nilpotent, it is also stored that it is solvable, see Chapters Types of Objects in the Reference Manual and Method Selection in ``Programming in GAP'' for the details.

  As a consequence, you cannot put your favourite information into a domain D by assigning it to a new component like ``D`.myPrivateInfo`. Instead you can introduce a new attribute and then use its setter, see Attributes in the Reference Manual.

  ## 9.9 Different Notions of Generation

  As in GAP 3, a domain in GAP 4 is a structured set.

  The same set can have different structures, for example a field can be regarded as a ring or as an algebra or vector space over a subfield.

  In GAP 3, however, an object representing a ring did not represent a field, and an object representing a field did not represent a ring. One reason for this was that the record component `generators` was used to denote the appropriate generators of the domain. For a ring R, the component ``R`.generators` was a list of ring generators, and for a field F, ``F`.generators` was a list of field generators.

  GAP 4 cleans this up, see Notions of Generation. It supports many different notions of generation, for example one can ask for magma generators of a group or for generators of a field as an additive group. A subtle but important distinction is that between generators of an algebra and of an algebra-with-one.

  So the attributes `GeneratorsOfGroup`, `GeneratorsOfMagma`, `GeneratorsOfRing`, `GeneratorsOfField`, `GeneratorsOfVectorSpace`, and so on, replace the access to the `generators` component.

  ## 9.10 Operations Records

  Already in GAP 3 there were several functions that were applicable to many different kinds of objects, for example `Size` could be applied to any domain, and the binary infix multiplication `*` could be used to multiply two matrices, an integer with a row vector, or a permutation with a permutation group. This was implemented as follows. Functions like `Size` and `*` tried to find out what situation was described by its arguments, and then it called a more specific function to compute the desired information. These more specific functions, let us call them methods as they are also called in GAP 4, were stored in so-called operations records of the arguments.

  For example, every domain in GAP 3 was represented as a record, and the operations record was stored in the record component `operations`. If `Size` was called for the domain then the method to compute the size of the domain was found as value of the `Size` component of the operations record.

  This was fine for functions taking only one argument, and in principle it is possible that for those functions an object stored an optimal method in its operations record. But in the case of more arguments this is not possible. In a multiplication of two objects in GAP 3, one had to choose between the methods stored in the operations records of the arguments, and if for example the method stored for the left operand was called, this method had to handle all possible right operands.

  So operations records turned out to be not flexible enough. In GAP 4, operations records are not supported (see Compatibility Mode for a possibility to use your GAP 3 code that utilizes operations records, at least to some extent). A detailed description of the new mechanism to select methods can be found in Chapter Method Selection in ``Programming in GAP''.

  An important point is that the new mechanism allows GAP to take the relation between arguments into account. So it is possible (and recommended) to install different methods for different relations between the arguments. Note that such methods need not do the extensive argument checking that was necessary in GAP 3, because most of the checks are done already by the method selection mechanism.

  ## 9.11 Operations vs. Dispatcher Functions

  GAP 3 functions like `Size`, `CommutatorSubgroup`, or `SylowSubgroup` did mainly call an appropriate method (see Operations Records) after they had checked their arguments. Such functions were called dispatchers in GAP 3. In GAP 4, many dispatchers have been replaced by operations, due to the fact that methods are no longer stored in operations records (see Method Selection in ``Programming in GAP'' for the details).

  Most dispatchers taking only one argument were treated in a special way in GAP 3, they had the additional task of storing computed values and using these values in subsequent calls. For example, the dispatcher `Size` first checked whether the size of the argument was already stored, and if so then this value was returned; otherwise a method was called, the value returned by this method was stored in the argument, and then returned by `Size`.

  In GAP 4, computed values of operations that take one argument (these operations are called attributes) are also stored, only the mechanism to achieve this has changed, see Attributes and Properties in the Reference Manual.

  So the behaviour of `Size` is the same in GAP 3 and GAP 4. But note that in GAP 4, it is not possible to access ``D`.size`, see Attributes vs. Record Components. As described in Operations Records, GAP 4 does not admit ``bypassing the dispatcher'' by calling for example ``D`.operations.Size`. This was done in GAP 3 often for efficiency reasons, but the method selection mechanism of GAP 4 is fast enough to make this unnecessary.

  If you had written your own dispatchers and put your own methods into existing operations records then this code will not work in GAP 4. See Creating New Objects and Method Selection in ``Programming in GAP'' for a description of how to define operations and to install methods.

  Finally, some functions in GAP 3 were hidden in operations records, e.g., `PermGroupOps.MovedPoints`. These functions became proper operations in GAP 4.

  ## 9.12 Parents and Subgroups

  In GAP 3 there was a strict distinction between parent groups and subgroups. The use of the name ``parent'' (instead of ``supergroup'') was chosen to indicate that the parent of an object was more than just useful information. In fact the main reason for the introduction of parents was to provide a common roof for example for all groups of polycyclic words that belonged to the same PC-presentation, or for all subgroups of a finitely presented group (see Elements of Finitely Presented Groups). A subgroup was never a parent group, and it was possible to create subgroups only of parent groups.

  In GAP 4 this common roof is provided already by the concept of families, see Families in the Reference Manual. Thus it is no longer compulsory to use parent groups at all. On the other hand, parents may be used in GAP 4 to provide information about an object, for example the normalizer of a group in its parent group may be stored as an attribute value. Note that there is no restriction on the supergroup that is set to be the parent, it is possible to create a subgroup of any group, this group then being the parent of the new subgroup. This permits for example chains of subgroups with respective parents, of arbitrary length.

  As a consequence, the `Parent` command cannot be used in GAP 4 to test whether the two arguments of `CommutatorSubgroup` fit together, this is now a question that concerns the relation between the families of the groups. So the 2-argument version of `Parent` and the now meaningless function `IsParent` have been abolished.

  ## 9.13 Homomorphisms vs. General Mappings

  In GAP 3 there had been a confusion between group homomorphisms and general mappings, as `GroupHomomorphismByImages` created only a general mapping that did not store whether it was a mapping. This caused expensive, unwanted, and unnecessary tests whether the mapping was in fact a group homomorphism. Moreover, the ``official'' workaround to set some components of the mapping record was quite unwieldy.

  In GAP 4, `GroupHomomorphismByImages` checks whether the desired mapping is indeed a group homomorphism; if so then this property is stored in the returned mapping, otherwise `fail` is returned. If you want to avoid the checks then you can use `GroupHomomorphismByImagesNC`. If you want to check whether a general mapping that respects the group operations is really a group homomorphism, you can construct it via `GroupGeneralMappingByImages` and then call `IsGroupHomomorphism` for it. (Note that `IsGroupHomomorphism` returns `true` if and only if both `IsGroupGeneralMapping` and `IsMapping` do, so one does in fact check `IsMapping` in this case.)

  There is no function `IsHomomorphism` in GAP 4, since there are several different operations with respect to which a mapping can be a homomorphism.

  ## 9.14 Homomorphisms vs. Factor Structures

  If F is a factor structure of G, with kernel N, complete information about the connection between F and G is provided by the natural homomorphism.

  In GAP 3, the ``official way'' to construct this natural homomorphism was to create first the factor structure F, and then to call `NaturalHomomorphism` with the arguments G and F. For that, the data necessary to compute the homomorphism was stored in F when F was constructed.

  In GAP 4, factor structures are not treated in a special way, in particular they do not store information about a homomorphism. Instead, the more natural way is taken to construct the natural homomorphism from G and N by `NaturalHomomorphismByNormalSubgroup` if N is a normal subgroup of the group G, or by `NaturalHomomorphismByIdeal` if N is an ideal in the ring G. The factor F can then be accessed as the image of this homomorphism, and of course G is the preimage and N is the kernel.

  Note that GAP 4 does not guarantee anything about the representation of the factor F, it may be a permutation group or a polycyclically presented group or another kind of group. Also note that a natural homomorphism need not be surjective.

  A consequence of this change is that GAP 4 does not allow you to construct a natural homomorphism from the groups G and F.

  The other common type of homomorphism in GAP 3, ``operation homomorphisms'', have been replaced (just a name change) by action homomorphisms, which are handled in a similar fashion. That is, an action homomorphism is constructed from an acting group, an action domain, and a function describing the operation. The permutation group arising by the induced action is then the image of this operation homomorphism.

  The GAP 3 function `Operation` is still supported, under the name `Action`, but from the original group and the result of `Action` it is not possible to construct the action homomorphism.

  ## 9.15 Isomorphisms vs. Isomorphic Structures

  In GAP 3, a different representation of a group could be obtained by calling `AgGroup` to get an isomorphic polycyclically presented group, `PermGroup` to get an isomorphic permutation group, and so on. The returned objects stored an isomorphism in the record component `bijection`.

  For the same reason as in Homomorphisms vs. Factor Structures, GAP 4 puts emphasis on the isomorphism, and the isomorphic object in the desired representation can be accessed as its image. So you can call `IsomorphismPcGroup` or `IsomorphismPermGroup` in order to get an isomorphism to a polycyclically presented group or a permutation group, respectively, and then call `Image` to get the isomorphic group.

  Note that the image of an action homomorphism with trivial kernel is also an isomorphic permutation group, but an action homomorphism need not be surjective, since it may be easier to define it into the full symmetric group.

  Further note that in GAP 3, a usual application of isomorphisms to polycyclically presented groups was to utilize the usually more effective algorithms for solvable groups. However, the new concept of polycyclic generating systems in GAP 4 makes it possible to apply these algorithms to arbitrary solvable groups, independent of the representation. For example, GAP 4 can handle polycyclic generating systems of solvable permutation groups. So in many cases, a change of the representation for efficiency reasons may be not necessary any longer.

  In general `IsomorphismFpGroup` will define a presentation on generators chosen by the algorithm. The corresponding elements of the original group can be obtained by the command

      gens:=List(GeneratorsOfGroup(Image(isofp)),i->PreImagesRepresentative(isofp,i));

  If a presentation in the given generators is needed, the command `IsomorphismFpGroupByGenerators(`G`,` gens`)` will produce one.

  ## 9.16 Elements of Finitely Presented Groups

  Strictly speaking, GAP 3 did not support elements of finitely presented groups. Instead, the ``words in abstract generators'' of the underlying free groups were (mis)used. This caused problems whenever calculations with elements were involved, the most obvious ones being wrong results of element comparisons. Also functions that should in principle work for any group were not applicable to finitely presented groups. In effect, a finitely presented group had to be treated in a special way in GAP 3.

  GAP 4 distinguishes free groups and their elements from finitely presented groups and their elements. Comparing two elements of a finitely presented group will yield either the correct result or no result at all.

  Note that in GAP 4, the arithmetic and comparison operations for group elements do not depend on a context provided by a group that contains the elements. In particular, in GAP 4 it is not meaningful to call `Order(` G`,` g`)` for a group G and an element g.

  ## 9.17 Polynomials

  In GAP 3, polynomials were defined over a field. So a polynomial over `GF(3)` was different from a polynomial over `GF(9)`, even if the coefficients were exactly the same.

  GAP 4 defines polynomials only over a characteristic. This makes it possible for example to multiply a polynomial over `GF(3)` with a polynomial over `GF(9)` without the need to convert the former to the larger field.

  However it has an effect on the result of `DefaultRing` for polynomials: In GAP 3 the default ring for a polynomial was the polynomial ring of the field over which the polynomial was defined. In GAP 4 no field is associated, so (to avoid having to define the algebraic closure as the only other sensible alternative) the default ring of a polynomial is the `DefaultRing` of its coefficients.

  This has an effect on `Factors`: If no ring is given, a polynomial is factorized over its `DefaultRing` and so `Factors(`poly`)` might return different results.

  To be safe from this problem, if you are not working over prime fields, rather call `Factors(`pring`,`poly`)` with the appropriate polynomial ring and change your code accordingly.

  ## 9.18 The Info Mechanism

  Sometimes it is useful to get information about the progress of a calculation. Many GAP functions contain statements to display such information under certain conditions.

  In GAP 3, these statements were calls to functions such as `InfoGroup1` or `InfoGroup2`, and if the user assigned `Print` to these variables then this had the effect to switch on the printing of information. `InfoGroup2` was used for more detailed information than `InfoGroup1`. One could switch off the printing again by assigning `Ignore` to the variables, and `Ignore` was also the default value.

  GAP 4 uses one function `Info` for the same purpose, which is a function that takes as first argument an info class such as `InfoGroup`, as second argument an info level, and the print statements as remaining arguments. The level of an info class class is set to level by calling `SetInfoLevel(` class`,` level`)`. An `Info` statement is printed only if its second argument is smaller than or equal to the current info level. For example,

      gap> test:= function( obj )
      > Info( InfoGroup, 2, "This is useful, isn't it?" );
      > return obj;
      > end;;
      gap> test( 1 );
      1
      gap> SetInfoLevel( InfoGroup, 2 );
      gap> test( 1 );
      #I  This is useful, isn't it?
      1

  As in GAP 3, if an info statement is ignored then its arguments are not evaluated.

  ## 9.19 Debugging

  If GAP 4 runs into an error or is interrupted, it enters a break loop. The command `Where(` number`)`, which replaces `Backtrace` of GAP 3, can be used to display number lines of information about the current function call stack.

  As in GAP 3, access is only possible to the variables of the current level in the function stack, but in GAP 4 the function `DownEnv`, with a positive or negative integer as argument, permits one to step down or up in the stack.

  When interrupting, the first line printed by `Where` actually may be one level higher, as the following example shows

      gap> OnBreak := function() Where(0); end;; # eliminate back-tracing on
      gap>                                       # entry to break loop
      gap> test:= function( n )
      >    if n > 3 then Error( "!\n" ); fi; test( n+1 ); end;;
      gap> test( 1 );
      Error, !
      Entering break read-eval-print loop ...
      you can 'quit;' to quit to outer loop, or
      you can 'return;' to continue
      brk> Where();
       called from
      test( n + 1 ); called from
      test( n + 1 ); called from
      test( n + 1 ); called from
      <function>( <arguments> ) called from read-eval-loop
      brk> n;
      4
      brk> DownEnv();
      brk> n;
      3
      brk> Where();
       called from
      test( n + 1 ); called from
      test( n + 1 ); called from
      <function>( <arguments> ) called from read-eval-loop
      brk> DownEnv( 2 );
      brk> n;
      1
      brk> Where();
       called from
      <function>( <arguments> ) called from read-eval-loop
      brk> DownEnv( -2 );
      brk> n;
      3
      brk> quit;
      gap> OnBreak := Where;; # restore OnBreak to its default value

  For purposes of debugging, it can be helpful sometimes, to see what information is stored within an object. In GAP 3 this was possible using `RecFields` because the objects in question were represented via records. For component objects, GAP 4 permits the same by `NamesOfComponents(` object`)`, which will list all components present.

  ## 9.20 Compatibility Mode

  For users who want to use GAP 3 code with as little changes as possible, a compatibility mode is provided by GAP 4. This mode must be turned on explicitly by the user.

  It should be noted that this compatibility mode has not been tested thoroughly.

  The compatibility mode can be turned on by loading some of the following files with `ReadLib`. The different files address different aspects of compatibility.

  `compat3a.g`
    makes some GAP 3 function names available that were changed in GAP 4, and provides code for some GAP 3 features that were deliberately left out from the GAP 4 library. For example, almost all variable names concerning character theory that are mentioned in the GAP 3 manual, such as `CharTable` and `SubgroupFusions`, are available after `compat3a.g` has been read; the only exceptions are names of operations records.

  `compat3b.g`
    implements the availability of ``components'' of domains; besides components that have no meaning for the rest of the GAP 4 library, such as ``D`.myInfo`, there are components associated to attributes; for example ``D`.size` is redirected to the call of the attribute `Size`, `IsBound(` D`.size )` to the call of its tester, and ``D`.size:=` val`` to the call of its setter. (An important special case is the component `operations`, see below.)

  `compat3c.g`
    permits you to implement your own elements represented as records, and using operations records to provide a `Print` method and the basic arithmetic operations. When using operations records, it is probably a good idea to use immutable operations records; for example, if the results of arithmetic operations are records with operations records then this avoids to create shallow copies of the operations records in the call to `Immutable` for the results.

  The following features are accessible only via starting GAP with the command line option `-O` and may damage some features of GAP 4 permanently for the current session.

  With this option, also the files listed above are read automatically.

  `compat3d.g`
    provides some GAP 3 functions like `Domain`, simulates the GAP 3 behaviour of `IsString` (to convert a list to string representation if possible), and replaces `fail` by `false`; these changes destroy parts of the functionality of GAP 4.

  Some words concerning the simulation of operations records may be necessary.

  The operations records of the GAP 3 library, such as `DomainOps` and `GroupOps`, are available only for access to their components, whose values are GAP 4 operations; for example, the value of both `DomainOps.Size` and `GroupOps.Size` is the operation `Size`. So it is not safely possible to delegate from a `Size` method in another operations record to `DomainOps.Size`. Also it is not possible to change these predefined operations records.

  If one wants to install individual methods for a given object obj via the mechanism of operations records then one can construct a new operations record with `OperationsRecord`, assign the desired methods to components of this record, and then assign the operations record to ``obj`.operations`. Whenever an operation that is associated with a component nam of the operations record is called with obj as first argument, the value of nam is chosen as the method.

  In the case of the binary operations `=`, `<`, `+`, `-`, `*`, `/`, `Comm`, and `LeftQuotient`, this also happens if obj is the right-hand argument. As in GAP 3, if both arguments of one of the above binary operations have operations records containing a function for this operation, then the function in the operations record of the right-hand argument is chosen.

  We give a small example how the compatibility mode works.

  Suppose we want to deal with new objects that are derived from known field elements by distorting their multiplication. Namely, let a′ and b′ be the new objects corresponding to the field elements a, b, and define a′* b′ = a b − a − b + 2.

  In GAP 3, this problem was solved by representing each new object by a record that stored the corresponding ``old'' object and an operations record, where the latter was a record containing the functions applicable to the new object. After the library file `compat3c.g` has been read, we can use this construction of the operations record and of the new objects. Note that operations records must be created with the function `OperationsRecord` (this was also the norm in GAP 3), starting with an empty record would not work. For our intended application, we thus start with the following two lines of code.

      gap> ReadLib( "compat3c.g" );
      gap> MyOps:= OperationsRecord( "MyOps" );;
      HasMyOps := NewFilter( "HasMyOps" );

  In order to make the translation from GAP 3 code to GAP 4 easier, GAP prints the definition of filters associated with operations records and the method installations for operations corresponding to components of the operations records. The output line printed by GAP after the call of `OperationsRecord` is one such case.

  Now we add our multiplication 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 - a.x - b.x + 2,
      >                    operations := MyOps );
      >    end;;
      # If the following method installation matches the requirements
      # of the operation `PROD' then `InstallMethod' should be used.
      # It might be useful to replace the rank `SUM_FLAGS' by `0'.
      InstallOtherMethod( PROD,
          "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 in GAP 3, the method is
      # installed with higher rank `SUM_FLAGS + 1'.
      InstallOtherMethod( PROD,
          "for object with `MyOps' as second argument",
          true,
          [ IsObject, HasMyOps ], SUM_FLAGS + 1,
          MyOps.\* );

  Let us look how this installation works.

      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 := 9, operations := MyOps )

  (In more complicated cases, we might run into problems, but this was already the case in GAP 3. For example, suppose we want to support the multiplication of two operands having different operations records; then it is not clear which of the two multiplication functions is to be chosen, and in GAP 3, the only way out was to change the multiplication functions, in order to make them aware of such situations.)

  If we are now interested to translate the code to GAP 4 in the sense that no compatibility mode is needed, we can use what GAP 4 has printed above. (The same example is dealt with in Chapter An Example -- Designing Arithmetic Operations of ``Programming in GAP''.)

  The objects will no longer be records with `operations` component. Instead of records we may use so-called component objects with record-like access to components, and instead of the `operations` component, we give the objects a type that has the filter `HasMyOps` set.

      HasMyOps := NewFilter( "HasMyOps" );
      MyType := NewType( NewFamily( "MyFamily" ),
                         HasMyOps and IsComponentObjectRep );

  (More about families and representations in this context can be found in the chapter of ``Programming in GAP'' mentioned above.)

  The next step is to write a function that creates a new object. It may look as follows.

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

  The multiplication function shall return an object with the filter `HasMyOp`, so we change it as follows.

      gap> MyMult := function( a, b )
      >        return MyObject( x:= a!.x * b!.x - a!.x - b!.x + 2 );
      >    end;;

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

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

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

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

  And now the example works (again).

      gap> a:= MyObject( 3 );
      <object>
      gap> b:= MyObject( 5 );
      <object>
      gap> a * b;
      <object>
      gap> last!.x
      9

  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( 9 )

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

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

  At first sight, the GAP 4 approach seems to be much more complicated. But the last example shows that in GAP 4, each method can be installed more specifically for the appropriate situation. Moreover, it is for example possible to install a method for the multiplication 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.

  Another example that uses this part of the compatibility mode can be found in the file `tst/compat3.tst` of the GAP 4 distribution.

  [Top] [Up] [Previous] [Next] [Index]

  GAP 4 manual
  March 2004