Skip to content

Adding new Built in Function

R. Bernstein edited this page Sep 11, 2021 · 2 revisions

This page needs major rewriting

Here is the process for adding new built-in Functions in Python to Mathics is very easy.

Use the structure of Mathematica to find an existing group to put it in. We will use If as an example. If is a in the Procedureal Programming group.

Create a new class derived from Builtin. To get an idea of how a built-in class can look like, consider the following implementation of If:

class If(Builtin):
	"""
	<dl>
	<dt>'If[$cond$, $pos$, $neg$]'
		<dd>returns $pos$ if $cond$ evaluates to 'True', and $neg$ if it evaluates to 'False'.
	<dt>'If[$cond$, $pos$, $neg$, $other$]'
		<dd>returns $other$ if $cond$ evaluates to neither 'True' nor 'False'.
	<dt>'If[$cond$, $pos$]'
		<dd>returns 'Null' if $cond$ evaluates to 'False'.
	</dl>
	>> If[1<2, a, b]
	 = a
	If the second branch is not specified, 'Null' is taken:
	>> If[1<2, a]
	 = a
	>> If[False, a] //FullForm
	 = Null

	You might use comments (inside '(*' and '*)') to make the branches of 'If' more readable:
	>> If[a, (*then*) b, (*else*) c];
	"""

	attributes = ['HoldRest']

	rules = {
	  'If[condition_, t_]': 'If[condition, t, Null]',
	}

	def apply_3(self, condition, t, f, evaluation):
		'If[condition_, t_, f_]'

		if condition == Symbol('True'):
			return t.evaluate(evaluation)
		elif condition == Symbol('False'):
			return f.evaluate(evaluation)

	def apply_4(self, condition, t, f, u, evaluation):
		'If[condition_, t_, f_, u_]'

		if condition == Symbol('True'):
			return t.evaluate(evaluation)
		elif condition == Symbol('False'):
			return f.evaluate(evaluation)
		else:
			return u.evaluate(evaluation)

The class starts with a Python docstring that specifies the documentation and tests for the symbol. A list (or tuple) attributes can be used to assign attributes to the symbol. Protected is assigned by default. A dictionary rules can be used to add custom rules that should be applied.

Python functions starting with apply are converted to built-in rules. Their docstring is compiled to the corresponding Mathics pattern. Pattern variables used in the pattern are passed to the Python function by their same name, plus an additional evaluation object. This object is needed to evaluate further expressions, print messages in the Python code, etc. Unsurprisingly, the return value of the Python function is the expression that is replaced for the matched pattern. If the function does not return any value, the Mathics expression is left unchanged. Note that you have to return Symbol["Null"] explicitly if you want that.

If you want to add an operator, you should use one of the subclasses of Operator. Use SympyFunction for symbols that have a special meaning in SymPy.