In [1]:

```
# using Symata # when starting a stock Julia image
isymata() # when starting the precompiled-with Symata image
```

This is a translation from *Mathematica* to *Symata* of the Introduction to "*Mathematica* programming: an advanced introduction" v 1.01 by Leonid Shifrin. Minimal changes to accomodate *Symata* were made. This text is licensed under the Creative Commons Attribution-Noncomercial-Share Alike 3.0 United States license. http://creativecommons.org/licenses/by-nc-sa/3.0/us/

*Mathematica* is a registered trademark of Wolfram Research Inc.

Despite the title, since this is only the introduction, there is almost nothing about programming here. Rather it illustrates some of the core principles of the language. These principles concern the part of *Symata* that follows *Mathematica* semantics. *Symata* also breaks and enhances *Mathematica* semantics by integrating smoothly with Julia.

Atoms are numbers, symbols and strings, and numbers are further divided into Integers, Reals, Rationals and Complex. All other objects are composite and are called Normal Expressions. It is always possible to check whether or not an expression is an atom or a composite, by acting on it with the built-in predicate AtomQ. For instance:

In [2]:

```
ClearAll()
[AtomQ(x), AtomQ(Sin(x)), AtomQ(1 + I * 2), AtomQ(2 / 3)]
```

Out[2]:

Every normal expression (composite) is built according to a universal pattern:
expr`[el1, ..., eln]`

Here it is required that some symbol `<expr>`

is present (it can itself be a normal expression, not necessar-
ily an atom), as well as the single square brackets. Inside the parentheses, there can be zero, one or
several comma-separated elements `<el1>,...,<eln>`

. These elements themselves can be either atoms or
normal expressions. In an expression `Sin(x)`

, `<expr>`

is Sin, and there is a single element `<x>`

, which is
atom (as long as x is not defined as something else, but this already has to do with expression evaluation
and will be discussed below). It is clear that an arbitrary *Symata* expression must have a tree-like
structure, with the branches being normal (sub)expressions and leaves being atoms.

As a consequence, any built-in command/function in *Symata* has a literal/string equivalent (so that it can be represented in the above uniform way). This is most easily seen with the help of the built-in function FullForm, which shows the internal representation of any object/expression, in the way it is really
"seen" by the kernel. For instance:

In [3]:

```
[z * Sin(x +y), FullForm(z * Sin(x +y))]
```

Out[3]:

The second expression in the square brackets is equivalent to the first one, but explicitly shows the structure described above.

In the following example `<expr>`

is
Times (the multiplication command):

In [4]:

```
a = z * Sin(x +y);
FullForm(a)
```

Out[4]:

In general, an expression outside the square brackets has a name - it is called a head of expression, or just head. There is a built-in function with the same name, which allows to obtain the head of an arbitrary expression. For example:

In [5]:

```
Head(a)
```

Out[5]:

A head of an expression may be either an atom or a normal expression itself. For example :

In [6]:

```
Clear(b, f, g, h, x);
b = f(g)(h)(x);
Head(b)
```

Out[6]:

In [7]:

```
Head(f(g)(h))
```

Out[7]:

In [8]:

```
Head(f(g))
```

Out[8]:

Every expression has a head, even atoms. Heads for them include String, Symbol, Integer, Real, Rational and Complex. For instance :

In [9]:

```
[Head(f), Head(2), Head(Pi), Head(3.14), Head("abc"), Head(2/3), Head(1 + I)]
```

Out[9]:

One can access also the internal parts of an expression (those inside the parentheses), by using indexing (Part command). The following example illustrates this.

In [10]:

```
[a[0], a[1], a[2], a[2, 0], a[2, 1], a[2, 1, 0], a[2, 1, 1], a[2, 1, 2]]
```

Out[10]:

We have just deconstructed our expression to pieces. In fact, we started from the "stem" and then went down along the "branches" to the "leaves" of the tree which we have seen above with the TreeForm. We see that the addresses (index sequences) which end with zero give the Heads of the subexpressions - this is a convention. In principle, any complex expression can be deconstructed in this way, and moreover, one can change its subexpressions.

It is also possible to get access to the branches (subexpressions) which are at the certain distance (level) from the "stem". This is achieved by using a built-in Level command. Consider an example:

In [11]:

```
Clear(a)
a = z * Sin(x + y) + z1 * Cos(x1 + y1)
```

Out[11]:

Here it is in its full form:

In [12]:

```
FullForm(a)
```

Out[12]:

These are the levels of the tree:

In [13]:

```
Level(a, [0])
```

Out[13]:

In [14]:

```
Level(a, [1])
```

Out[14]:

In [15]:

```
Level(a, [2])
```

Out[15]:

In [16]:

```
Level(a, [3])
```

Out[16]:

In [17]:

```
Level(a, [4])
```

Out[17]:

`Level[a, {n}]`

gives all branches (or leaves) which have a distance of n levels down from the "stem". If
however we need all branches that have n levels of sub - branches (or leaves), then we use a negative level
`Level[a, {-n}]`

:

In [18]:

```
Level(a, [-1])
```

Out[18]:

In [19]:

```
Level(a, [-2])
```

Out[19]:

In [20]:

```
Level(a, [-3])
```

Out[20]:

In [21]:

```
Level(a, [-4])
```

Out[21]:

Notice that negative levels generally can not be reduced to positive levels - they are giving in general
different types of information. What we have just described is called the Standard Level Specification in
*Symata*. Many more built - in commands accept level specification as one of the arguments (often an
optional one).

Any function can be used also in its literal equivalent form. For instance :

In [22]:

```
[Plus(1, 2, 3, 4), Times(1, 2, 3, 4)]
```

Out[22]:

Another fundamental principle is so - called pattern - matching, which is a system to match rules and
expressions - without it *Symata* would not know when to apply which rule. It is based on syntactic
rather than semantic comparison of expressions. The main notions here are those of rules and patterns.

In [23]:

```
Clear(a, b, c, d, e)
```

A typical *rule* looks like this:

In [24]:

```
a => b
```

Out[24]:

where in general `<a>`

and `<b>`

are some expressions. The rule just says: whenever `<a>`

is
encountered, replace it by `<b>`

. For example:

In [25]:

```
[a, c, d, c] ./ (a => b)
```

Out[25]:

(the `./`

symbol is a rule replacement command, to be covered later).

A pattern is essentially any expression with some part of it replaced by "blank" (`Blank()`

), which is a
placeholder for any expression - that is, instead of that part there can be anything (this is somewhat oversim-
plified). The literal equivalent for `Blank[]`

is the single underscore ("`_`

") symbol. For instance, `f(x_)`

means `f(anything)`

.

In [26]:

```
Clear(f)
f(x_) := x^2
[f(2), f("word"), f(Newton)]
```

Out[26]:

In this example, the result is as shown because the definition of the function `f`

is really just a substitution rule `f(anything) -> (anything)^2`

.

To see the internal form of this rule - how it is stored in the rule base - one can use the built-in DownValues command. With its help we see:

In [27]:

```
DownValues(f)
```

Out[27]:

We will talk later about the meaning of the `HoldPattern`

function. The pattern `x_`

is the most simple pattern. There can be more complex patterns, both syntactically and also because patterns may have conditions attached to them, which ensure that the pattern will match only if the condition is satisfied
(conditional patterns). We will cover them in detail later.

Now let us give an example: we will restrict our function `f`

to operate only on integers.

In [28]:

```
Clear(f)
f(x_Integer) := x^2
[f(2), f("word"), f(Newton)]
```

Out[28]:

In this case, we introduced a more complex pattern `x_Integer`

.

On this example we see that if there is no rule whose pattern (left hand side of the rule) matches a given
expression, *Symata* returns the expression unchanged. This is at the heart of its evaluation method:
to any entered expression, all rules which are in the global rule base at the moment of evaluation, are
applied iteratively. Whenever some rule applies, an expression is rewritten and the process starts over.
At some point, the expression becomes such that no rule can be applied to it, and this expression is the
result. Since the rule base contains both system and user-defined rules (with the latter having higher
priority), it gives great flexibility in manipulation of expressions.

As another starting example, let us define a function which is linear on even numbers, quadratic on odd numbers and is a Sin function for all other inputs:

In [29]:

```
Clear(f)
f(x_`EvenQ`) := x
f(x_`OddQ`) := x^2
f(x_) := Sin(x)
```

In [30]:

```
[f(1), f(2), f(3), f(4), f(3 / 2), f(Newton), f(Pi)]
```

Out[30]:

For the record, built-in functions `OddQ`

and `EvenQ`

are predicates which return `True`

if the number is odd
(even) and `False`

otherwise :

In [31]:

```
[EvenQ(2), EvenQ(3), OddQ(2), OddQ(3)]
```

Out[31]:

If nothing is known about the object, they give False :

In [32]:

```
[EvenQ(Newton), OddQ(Newton)]
```

Out[32]:

Automatic rule reordering is *not* yet implemented:

In [33]:

```
Clear(f)
f(x_) := Sin(x)
f(x_`EvenQ`) := x
f(x_`OddQ`) := x^2
[f(1), f(2), f(3), f(4), f(3 / 2), f(Newton), f(Pi)]
```

Out[33]:

To see the order in which the rules are kept, we again use DownValues :

In [34]:

```
DownValues(f)
```

Out[34]:

The last example brings us to the third principle: the principle of expression evaluation and the rewrite
rules (global rule base). It tells the following: when *Symata* encounters an arbitrary expression, it
checks its global base of rewrite rules for rule(s) which correspond to a given expression (or, it is said,
match the expression). A typical rewrite rule looks like `object1 -> object2`

. If such a rule is found, for
expression or any of the subexpressions (actually, normally in reverse order), the (sub) expression is
rewritten, and the process starts over. This process goes on until no further rule in the global rule base is
found which matches the expression or any of its parts. When the expression stops changing, it is returned
as the answer. Please bear in mind that the picture just described is a great oversimplification, and the real
evaluation process is much more subtle, although the main idea is this.

The global rule base contains both rules built in the kernel and rules defined by the user. User-defined rules usually take precedence over the system rules, which makes it possible to redefine the behavior of almost any built-in function if necessary. In fact, all assignments to all variables and all function defini- tions are stored as some type of global rules, in the rule base. In this sense, there is no fundamental differ- ence between functions and variables (although there are technical differences).

As a result of this behavior, we get for instance such a result:

In [35]:

```
FullForm(Sin(Pi + Pi))
```

Out[35]:

The reason is that inside the kernel there are rules like `Plus[x,x]->2 x`

, `Sin[2*Pi]->0`

, and because the
evaluation process by default starts with the innermost sub-expressions (leaves), i.e., from inside out, it
produces 0 before the `FullForm`

has any chance to "look" at the expression. The internal evaluation dynamics can be monitored with the `Trace`

command:

In [36]:

```
Trace(True)
FullForm(Sin(Pi + Pi))
```

Out[36]:

In [37]:

```
Trace(False);
```

To summarize, we have described briefly the main principles of Mathematica and along the way gave
examples of use of the following built-in functions: `AtomQ`

, `Head`

, `FullForm`

, `Level`

, `Plus`

, `Times`

, `Trace`

, `DownValues`

, `OddQ`

, `EvenQ`

.

In [38]:

```
VersionInfo()
```

In [39]:

```
InputForm(Now())
```

Out[39]: