Dear GAP Forum,
Sergei Haller asked a number of questions about mutable and immutable objects 
and copying.
The intended meaning of immutability in GAP4 is a  mathematical one. Any GAP 
object can be thought of as a representation of an "abstract" mathematical 
object of some kind. We call these "mathematical objects" Elements and they 
can also be viewed as equivalence classes of GAP objects under the relations 
\=. An immutable object should never change in such a way that it represents a 
different element.
This is enforced in different ways for built-in objects (like records, or 
lists) and for external objects (made using Objectify).
For built-in objects which are flagged as Immutable, the kernel will prevent 
you from changing them. Thus
gap> l := [1,2,4];
[ 1, 2, 4 ]
gap> MakeImmutable(l);
gap> l[3] := 5;
Lists Assignment: <list> must be a mutable list
not in any function
Entering break read-eval-print loop, you can 'quit;' to quit to outer loop,
or you can return and ignore the assignment to continue
brk> 
For external objects, the situation is different. An external object which 
claims to be immutable (ie its type does not contain the filter IsMutable) 
should not admit any Methods which change the Element it represents. The 
kernel does not prevent the use of !. and ![ to change the underlying data 
structure. This is used for instance by the code that stores Attribute values 
for reuse.  In general, these ! operations should only be used in Methods 
which depend on the representation of the object. Furthermore, we would NOT 
recommend users to install methods which depend on the representations of 
objects created by the library or by share packages, as there is certainly no 
guarantee of the representations being the same in future versions of GAP.
Here we see an immutable object (the group S4), in which we "improperly" 
install a new component.
gap> g := SymmetricGroup(IsPermGroup,4);
Sym( [ 1 .. 4 ] )
gap> IsMutable(g);
false
gap> NamesOfComponents(g);
[ "GeneratorsOfMagmaWithInverses", "Size", "MovedPoints", "NrMovedPoints" ]
gap> g!.silly := "rubbish";
"rubbish"
gap> NamesOfComponents(g); 
[ "GeneratorsOfMagmaWithInverses", "Size", "MovedPoints", "NrMovedPoints", 
  "silly" ]
gap> g!.silly;
"rubbish"
On the other hand, if we form an immutable externally represented list, we 
find that there is no Method for changing it using list assignment
gap> e := Enumerator(g);
<enumerator of perm group>
gap> IsMutable(e);
false
gap> IsList(e);
true
gap> e[3];
(1,4)(2,3)
gap> e[3] := false;
Error, no method found! For debugging hints type ?Recovery from NoMethodFound
Error no 1st choice method found for `ASS_LIST' on 3 arguments at
Finally we come to the question of copying. Here ShallowCopy and 
StructuralCopy behave quite differently, and another filter, IsCopyable, 
enters the game.
Objects can be divided for this purpose into three: mutable objects, immutable 
but copyable objects, and non-copyable objects (called constants).
A mutable or copyable  object should have a Method for the Operation 
ShallowCopy, which should make a new mutable object, sharing its top-level 
subobjects with the original. The exact definition of top-level subobject may 
be defined by the implementor for new kinds of object.
ShallowCopy applied to a constant simply returns the constant.
StructuralCopy is expected to be much less used than ShallowCopy. Applied to a 
mutable object, it returns a new mutable object which shares no mutable 
sub-objects with the input. Applied to an immutable object, it just returns 
the object. It is not an Operation (indeed, it's a rather special kernel 
function).
gap> e1 := StructuralCopy(e); <enumerator of perm group> gap> IsMutable(e1); false gap> e2 := ShallowCopy(e); [ (), (1,2)(3,4), (1,4)(2,3), (1,3)(2,4), (2,3,4), (1,2,4), (1,4,3), (1,3,2), (2,4,3), (1,2,3), (1,4,2), (1,3,4), (3,4), (1,2), (1,4,2,3), (1,3,2,4), (2,3), (1,2,4,3), (1,4), (1,3,4,2), (2,4), (1,2,3,4), (1,4,3,2), (1,3) ] gap>
Immutable makes a new immutable object which shares no mutable subobjects with 
the input.
MakeImmutable changes an object and its mutable subobjects in place to be 
immutable. It should only be used on "new" objects that you have just created.
Both Immutable and MakeImmutable work on external objects by just reseting the 
IsMutable filter. This should make ineligible any methods that might change 
the objects. As a consequence, you must allow for the possibility of immutable 
versions of any objects you create.
So, if you are implementing your own external objects. The rules amount to the 
following:
1. You decide if your objects should be mutable or copyable or constants, by 
fixing whether their type includes IsMutable or IsCopyable
2. You install Methods for your objects respecting that decision:
* for constants -- no methods that change the underlying elements
* for copyables -- a method for ShallowCopy
* for mutables  -- you may have methods that change the underlying elements
		   these should explicitly require IsMutable
Steve