Traditional imperative languages like C, Pascal etc. have value semantic: variables store possibly
complicated compound values. Normal calling sequence copies values of function parameters so
that assignment to parameters do not affect values inside calling function. Value semantic
is easy to understand, but copies may be costly both due to extra space and time needed for
copying. Sometimes correct behaviour is to modify shared value. To get effect of sharing
classical languages use pointers (at lowest level pointers are just machine addresses).
In imperative languages data structures may be modified at almost any time, so to avoid
unwanted modification one has to copy data. Value semantics plays nicely with need to
copy: by default one gets safe behaviour and to get sharing one need an explicit action
(apropriate pointer operations). In pure functional language at logical level there is no
modification of data, so one can safely share data structures. Internally implementation
of pure functional language may use pointers, but they are invisible to programmer.
Nice side effect of hidden pointers is that all data appears to be of the same size:
for example list of 100 numbers is represented via pointer and the pointer can be
stored in place of single number. When functional programming is mixed with imperative
constructs sharing via hidden pointers becomes visible as changed semantics.
This is called reference semantics.
For example:
fricas
(1) -> a1 := new(3, 1)$Vector(Integer)
Type: Vector(Integer)
fricas
a2 := a1
Type: Vector(Integer)
fricas
a2(2) := 3
fricas
a1
Type: Vector(Integer)
Above, modification to a2 is visible as modification to a1. This is because variable merely
store reference to actual data. Assigment "a2 := a1" just copies reference, without any
change to data.
In FriCAS all data have reference semantics. Integers are considered to be immutable objects
so conceptually assigning new integer value to variable means that new integer value is
created and reference to this integer is stored in variable. To optimize, small enough
integers are stored directly in variables and implementation uses low-level tricks to
distinguish references from integers. At higher level user sees the same behaviour,
integers behave the same regardless if they are stored directly in variables or
they are stored separately and variables only contains references. Similarly,
boolean values are stored directly in variables. OTOH records, unions, lists
and arrays always store actual data in separate area and variables just reference
this extra data.
Reference semantics works well with functional style: as long as data is not modified
after creation it can be safely shared and reference semantics means that
in many cases sharing will be automatic. When modifying data one have to
be careful to avoid unwanted modification. In particular, normally modification
is applied to copy. Copying have to be explicit:
fricas
a3 := copy(a2)
Type: Vector(Integer)
fricas
a3(1) := 7
fricas
a1
Type: Vector(Integer)
fricas
a2
Type: Vector(Integer)
fricas
a3
Type: Vector(Integer)
Sometimes for efficiency FriCAS functions modify data pointed to by references. By convention
functions doing this have !
at the end on name. When function is generic exact nature
of modification and if it is beneficial depends on actual types. In such cases function
return possibly new value (reference). It may point to old modified data, or may be new.
For such functions users should use returned value, treating argument as possibly mangled.
In terminology of Design By Contract contract of function involves returning reference
to correct data, but function makes no promise about future usability of argument.