Functional Modelling System
Tuples and Constructors
FMS can handle more data than just numbers, strings and Booleans. In this section we introduce more advanced types.
Tuples
Constructing tuples is straightforward: just write a tuple of expressions like this: (x,y,z)
. Tuples of any arity (except 1) are supported. This means that the empty tuple ()
exists. As wel as the 8-ary tuple (0,3,4,9,7,6,1,5)
exists.
When you get a tuple as input you should also be able to deconstruct it. You can do this in a number of ways.
- The simplest one is to use the tuple notation at the lefthand side of a definition.
(a,b) := x
. This means that you definea
to be the first component of the binary tuplex
andb
to be the second one. - Tuple deconstruction is also possible as locally scoped argument of a function definiton, in which case you assume a function to take tuples as its input. For instance,
f (a,b) := a*b
is a unary function which takes a binary tuple and multiplies its components. This also works with anonymous functions\(a,b) -> a * b
. - You can use a case-construct to deconstruct the tuple.
case x of (a,b) -> a+b;
. This expressions deconstructs the tuplex
and locally introduces the new variablesa
andb
to evaluate the expressiona+b
, which is the evaluation of this tuple.
Warning: Tuple deconstructions are always definitions, which never form constraints on their own. As such, you can not deconstruct a tuple with (a,a) := x
to enforce that the components of x
are equal. Use (a,b) := x
and a = b
instead.
Constructors
It is easy to get lost in the data when a lot of tuples are nested. For this reason we can use constructors. They are declared like this: s/1 :: constructor
for a unary constructor named s
. Constructors without arguments are also supported nil/0 :: constructor
.
All the techniques for deconstructing tuples generalize to constructors, except that you need to use square brackets [
]
instead of round brackets (
)
.
- You can pattern match the left side of the definition like this:
s [x] := y.
- Using patterns in the function definition is also allowed.
- Case-constructs also work for constructed types, it is even possible to match multiple patterns in one case like this
case x of s[y] -> y ; nil [] -> z;.
Patterns are handled top to bottom, and the first pattern that matches corresponds to the value of the case expression. So the expressioncase x of s[s[nil []]] -> 5 ; s[x] -> 2 ; x -> 0;.
will map the Peano representation of 2 to 5, all other successors to 2, and everything which is not a successor of something to 0.
Note again that square brackets are used when deconstructing constructors. This serves to differentiate between f x := y.
which declares a new function f
which is the constant function to y
and f [x] := y.
which declares the new symbol x
as removing the outer constructor f
from y
.
Using s
and nil
we can now do some Peano arithmetic.
More cases
The case-construct can be used in even more circumstances. In the matching part of a case expression you can also write a fixed number or string. An underscore is handled as a don't care-pattern and matches everything.
You can split definitions in to multiple lines as syntactical sugar for a case-construct. This allows us to for example define the factorial function.