FriCAS language (both interpreter and Spad) is imperative, basic operation is an assignment like
fricas
(1) -> a : Integer := 5
Type: Integer
fricas
b : Integer := a + 2
Type: Integer
FriCAS language is typed, each variable and expression have type, assigned value must have
correct type. Above we explicitly declared a
and b
to be of type Integer
. In same cases FriCAS can
infer the correct type and allows use of variables without explicit declaration. Interpreter
tries to assign reasonable type guessing user intent. Remark: this is quite different than type
inference present in same languages.
Next element of FriCAS language are expression. In first approximation expressions can be viewed
as tree of function calls with variables in the leaves. Remark: FriCAS treats various operators like functions. So *
is a two
argument function. The difference compared to ordinary functions is syntax, *
is parsed as infix
operator, but after parsing is indistingiushable from a function. So a+b*c
is eqivalent to
f(a, g(b,c))
where f
denotes addition (+
) and g
denotes multiplication (*
). Actually,
FriCAS expressions are more complicated, one can use almost any part of FriCAS syntax inside an
expression, but simple tree view is enough for basic understanding.
FriCAS supports overloading, that is one can give different definition of a function for various
combinations of parameter types. For example, with declarations above a*b
is multiplication
of integers. However, if c
is vector of integer, than a*c
means multiplication of vector
c
by integer a
which is a different operation despite having the same name. Remark: FriCAS
treats various operators like functions. So *
is a two argument function. The difference
compared to ordinary functions is syntax, *
is parsed as infix operator, but after parsing
is indistinguishable from a function.
FriCAS types are parametric, for example Polynomial(Integer)
is application of constructor
Polynomial
to parameter Integer
. Polynomial(Fraction(Integer))
is a different type
obtained using the same constructor, but with different parameter, that is Fraction(Integer)
,
which is application of constructor Fraction
to type Integer
. Parameter usually are
types, but it is also possible to use normal values as parameters.
Parametric types lead to parametric polymorphizm. This is similar to overloading, but conceptually
and in implementation there is a difference. For example, writing a + b
with integer a
and b
we use +
declared in AbelianSemigroup?. When adding two polynomials we use the same declaration,
but implementation is different. Choosing correct implementation is due to dispatch, from
compiler point of view we have single operation, but at runtime support routines are responsible
for calling correct version. Of course, users may worry that function calls in FriCAS need to
do complicated things to find correct function and are expensive. However, FriCAS contains
caching mechanism, first call may be quite expensive but usually subsequent executions of the
same call have only very small overhead. Beside parametric polymorphizm there is also
classical overloading. For example multiplication of vector by integer uses different
declaration than multiplication of integers and FriCAS compiler choose proper declaration
at compile time.
FriCAS has usual imperative constructs: sequence of operations, conditionals and loops.
FriCAS allows also FunctionalProgramming: FriCAS functions are "first class", one can
pass functions as arguments to other functions, store them in variables and data structures.
One can define local functions with capture references to variables in surrounding environment.
There are usual functional utilities like map
.
FriCAS types are "first class": parametric types allow creating potentially infinite number of
new types at runtime, types can be stored in variables and data structures. Types can be
passed to functions as arguments.
FriCAS is strongly typed in sense that all variables and expressions have assigned type, function
call is allowed only when arguments are of apropriate types. Strict type disciples sometimes
does on allow to express desired and correct code. FriCAS allows holes in type checking,
maininly via pretend
construct. For example
fricas
a pretend PositiveInteger
tells FriCAS that a
which is declared as Integer
should be treated as PositiveInteger
in expression above. Use of pretend
should be limited to cases when user knows that
value is actually of appropriate type, but FriCAS can not infer this in authomatic way.
Improper use of pretend
is an error which may lead to wrong value or a crash.
Basic FriCAS types are implemented at lower level, currently in Lisp. To allow such implementation
FriCAS allows direct calls to Lisp functions. Such calls are not type checked: FriCAS assumes
on faith that argument have correct Lisp type and result is of correct FriCAS type. Note:
FriCAS uses Lisp type declarations. Passing FriCAS value to Lisp function must respect
declarations passed to Lisp. If user call to Lisp function cause type mismatch at Lisp
level this may lead to trouble. In best case Lisp will report mismatch, but wrong result
or a crash are also possible. Typical use of Lisp calls is via Spad wrapper: wrapper
ensures that only correct types are passed to Lisp function and ensures correct FriCAS
type for result.