Evaluation of an Expression¶
In contrast to the simplicity and regularity for representing the data
for Expression
, evaluation of this data or expresion is a bit more
involved than conventional programming languages. I suppose this is to
be expected.
Part of the complexity revolves around the fact that the way function method lookup works is by pattern matching the expression. Also, there can be rule-based term-rewriting which goes on in conjunction with method lookup.
If you have programmed in WL, aside from the the Python-syntax and conventions used here, a lot of this should seem familiar,
If however you are not familiar with WL, but very familiar with Python
or similar languages, a lot of this can seem very mysterious at first:
functions don’t get called using a traditional way where you create an
object like Number()
and then instantiate a method on that, like
+
, __plus__()
, or even Times()
.
Of course, since the underlying interpreter language is Python, Python object creation and method lookup on that does happen. But it happens in a much more roundabout way.
For Python and Object-Oriented programmers, as an analogy for the
complexity and indirectness, an Object-Oriented “method dispatch” is
analogous. In Python or any Object-Oriented programming language, when
you write a.b()
: there is a method lookup in the a
object, so
at runtime the type of a
has to be inspected. And after having
that, the method handle b
needs to be computed. And this comes
from a class heirarchy.
Mathics and WL are not Object Oriented, so there is no such class-hierarchy lookup. However, as mentioned above, pattern matching is used to decide which method of the object to call.
Function Name to Python method lookup¶
When an Expression
has not been rewritten, the entire function
invocation in Mathics comes from the first leaf (or Head[]
) which
should be a Symbol
. In Python this will be a class some sort, such
as Builtin
or Predefined
or SympyFunction
. These classes
are described in a later section.
The remaining leaves of the Expression
are the parameters to give
to an apply
method.
In the simplest case, the evaluate()
method is called. This is
used when a function has no parameters or arguments. In other words,
it looks like a constant or variable name, and usually is prefaced
with a $`. Examples here are ``$VersionNumber
or $MachineName
.
Functions which take no parameters are generally subclassed off of the
Builtin
class.
However when a function takes parameters it method’s Object class is
derived either directly indirectly from the Predefined
class
rather than the Builtin
. To figure out which apply
method in
the class object to call, each method’s document string (or docstring)
is consulted. The lookup process is kicked off using the
evaluate()
method found in the Predefined
class.
As we go along, we’ll describe other conventions that are used that
are crucial in getting the interpreter work properly. But for now,
just remember that unless there is an evaluate()
method, there is
a method name in a Mathics function class that begins with apply
,
and its docstring is used to figure out whether the leaves of the list
are applicable to that function.
Here is an example for the Environment primitive taken from the code
class Environment(Builtin):
def apply(self, var, evaluation):
"""Environment[var_?StringQ]"""
...
The apply()
function above will get called when finding a
Expression
whose Head
value is Environment
and it has one
leaf or parameter which which we will call var
. That leaf or
parameter should also much be a String
object.
For more information describing Mathics function signatures that are
used in the apply
method’s docstring , see Functions and Programs
and Patterns.
Function Name Descriptions¶
Online and printed documentation for builtin Environment
comes from the docstring for class Environment
if that exists.
In the example above, it was omitted. Here is what it looks like in the actual code.
class Environment(Builtin):
"""
<dl>
<dt>'Environment[$var$]'
<dd>gives the value of an operating system environment variable.
</dl>
X> Environment["HOME"]
= ...
"""
def apply(self, var, evaluation):
<dl>
<dt>'Environment[$var$]'
<dd>gives the value of an operating system environment variable.
</dl>
X> Environment["HOME"]
= ...
""""
The XML/HTML markup is used to format help nicely. “Documentation markup” elsewhere describes this markup.
Python Code for Evaluating an Expression¶
Building on the code shown above for parsing an expression, here is code to evaluate an expression from a string:
# The below is a repeat of the parsing code...
from mathics.core.parser import parse, SingleLineFeeder
from mathics.core.definitions import Definitions
definitions = Definitions(add_builtin=True)
str_expression = "1 + 2 / 3"
expr = parse(definitions, SingleLineFeeder(str_expression))
# This code is new...
from mathics.core.evaluation import Evaluation
evaluation = Evaluation(definitions=definitions, catch_interrupt=False)
last_result = expr.evaluate(evaluation)
print("type", type(last_result))
print("expr: ", last_result)
Running the above produces:
type <class 'mathics.core.expression.Rational'>
expr: 5/3
All of the above is wrapped nicely in the module mathics.session
which
performs the above. So here is an equivalent program:
from mathics.session import session
str_expression = "1 + 2 / 3"
result = session.evaluate(str_expression)
Object Classes¶
The fundamental classes that functions are built up from are described below. Most of these classes are defined in mathics.builtin.base.
Atom Class Attributes¶
Recall that an Expression to be evaluated is kind of S-expression
called and ExpressionList
, where each list item is either itself
an ExpressionList
or an object in a class derived from Atom
.
The Atom
class we encountered earlier when describing the nodes
that get created intially from a parse. However there are a few other
kinds of Atoms or fundamention objects that can appear in an
Evaluation list. These are
CompiledCode
Image
Builtin and Predefined¶
Most of the functions loaded when Mathics starts up and before any
packages are loaded are either Builtin
or Predefined
Predefined
is a subclass of Builtin
.
A feature of the Predefined
class class is the convention that its
evaluation()
method looks at the docstring of methods that start
out with applied
in order to figure out which method to call
To be continued…