Dear Mrs. and Mr. Forum,
many things have changed from GAP-3.1 to GAP-3.2, and there are a few
cases where this means not only extensions but the possibility that a
function will cause strange results in GAP-3.2 although it worked well
in GAP-3.1. Maybe the most nasty case is the behaviour of strings.
As said in the announcement of GAP-3.2, now strings are also lists, thus
'IsList( <str> )' yields 'true' for a string <str>. Suppose you have a
function that shall append a suffix to strings, and whose argument can be
either a string or a list of strings, then in GAP-3.1 it may look like this.
gap> test := function( obj ) > if IsList( obj ) then > return List( obj, y -> ConcatenationString( y, ".suffix" ) ); > else > return ConcatenationString( obj, ".suffix" ); > fi; > end;; gap> test( "nolist" ); "nolist.suffix" gap> test( [ "l", "i", "s", "t" ] ); [ "l.suffix", "i.suffix", "s.suffix", "t.suffix" ]
In GAP-3.2 this function does not work.
gap> test( "nolist" ); Error, Append: <list2> must be a list at Append( res, str ) ... in ConcatenationString( y, ".suffix" ) called from fun( i ) called from List( obj, function ( y ) ... end ) called from test( "nolist" ) called from main loop brk> y; 'n' brk> quit; gap> test( [ "l", "i", "s", "t" ] ); [ "l.suffix", "i.suffix", "s.suffix", "t.suffix" ] gap> [ 'l', 'i', 's', 't' ]; "list" gap> List( last ); [ "l", "i", "s", "t" ]
This is because the string '"nolist"' now is a list, and 'test' tries to
concatenate its entries with the suffix; but the entries of '"nolist"'
aren't strings but characters (which are printed in single quotes).
So a string is equivalent to the list of its characters. On the other
hand, a list of strings is not itself a string; as in GAP-3.1, the function
'List' returns for a string the list of strings containing single letters.
Of course this is not the real problem, the solution is simply to ask for
the more special case of being a string first. But there is a problem, and
as everybody will suspect who read the messages to the GAP forum last week,
it is a problem with empties.
gap> IsList( "" ); IsString( "" ); true true gap> IsList(  ); IsString(  ); true true gap>  = ""; true
(By the way, what should the function 'test' defined above return in case
of input '""' or ''? In GAP-3.1 there was a well-defined difference.)
Empty string '""' and empty list '' are the same now, with one exception:
In contrary to nonempty lists of characters, the empty list is not converted
into a string, it is printed as '[ ]'. And printing one of these objects
is the problem. Suppose you want to write a program that prints strings
enclosed in '"', and prints other objects as they are printed by 'Print',
in GAP-3.1 this function worked.
gap> MyPrint := function( obj ) > if IsString( obj ) then > Print( "\"", obj, "\"\n" ); > else > Print( obj, "\n" ); > fi; > end;; gap> MyPrint(  ); "[ ]" gap> MyPrint( "" ); ""
We want the empty list to be printed as '[ ]', and the empty string as '""',
so we must be able to distinguish the two objects. The only solution is to
ask for the information that also GAP-3.2 uses to distinguish them.
This can be done using the function 'TYPE' that returns '"string"' for '""',
and '"list"' for ''. Note that this is one of the VERY FEW situations
where an undocumented function like 'TYPE' is of interest for the user,
normally one should avoid using such functions.
gap> MyPrint := function( obj ) > if IsString( obj ) and TYPE( obj ) = "string" then > Print( "\"", obj, "\"\n" ); > else > Print( obj, "\n" ); > fi; > end;; gap> MyPrint(  ); [ ] gap> MyPrint( "" ); ""