Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,9 @@ In a cloned git directory, you can find some of missing using ``git grep -n '^#
Below, we have a few examples of some actual Mathics3 Builtins that we have added. Hopefully you can use this as an aid for filling in one of the many Builtin Functions, such as one of the above.


.. toctree::
:maxdepth: 1

Undefined/index
KroneckerProduct/index
Curl/index
* :ref: `Undefined`
* :ref: `KroneckerProduct`
* :ref: `Curl`


As we added these Builtins, we recorded the steps that were taken. We ordered the list above to go from the simple to more advanced.
Expand Down
13 changes: 7 additions & 6 deletions docs/extending/developing-code/extending/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ After reading this you may want to go through the detailed examples in :ref:`Cas

tutorial/0-predefined
tutorial/1-builtin
tutorial/2-help-markup
tutorial/3-test-markup
tutorial/4-patterns
tutorial/5-rules
tutorial/6-attributes
tutorial/7-warnings
tutorial/2-parameter-checking
tutorial/3-help-markup
tutorial/4-test-markup
tutorial/5-patterns
tutorial/6-rules
tutorial/7-attributes
tutorial/8-warnings

.. TODO: Document Operator and SympyFunction
.. TODO: Document interrupts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ The docstring for the ``eval`` method is a Mathics3 pattern:

"Hello[person_String]"

Inside Mathic3 you can test what this matches using the Mathics3 ``MatchQ[]`` function:
Inside Mathics3, you can test what this matches using the Mathics3 ``MatchQ[]`` function:

.. code-block:: mathematica

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
Checking Parameter Counts
-------------------------

.. index:: parameter count checking

In a programming language that allows for generic and extensible
arguments, what should we do when a function parameter list does not
match any of the patterns of a particular builtin function specification?

In WMA and systems with rewrite rules, often what is done is the
expression is returned unchanged. This allows users to write rewrite rules
involving patterns that are not covered by your code.

For example:

.. code-block:: mathematica

In[1]:= Hello[] = "Hi, Rocky!";

In[2]= Hello[]
Out[2]= "Hi, Rocky!"

Above, in essence we've added form of *Hello* that does not need a parameter.
Instead, it provides a default value when none is given.

Pretty convenient and slick huh? Yes, but it can also lead to
confusion in trying to find out where a problem is coming from since
the definition can be spread to from many sources of definitions and
rewrite rules which might be mentioned or part of other definitions:

.. code-block:: mathematica

In[3]:= Rocky[Hello[]] = "Hi again, Rocky";

In[3]= Rocky[Hello[]]
Out[3]= "Hi again, Rocky"

Notice that the *In[3]* assignment takes place before the *In[1]*
assignment. Here, these rewrite rules are in close proximity, but
they could be spread out over time or over different files.

To help you figure how what evaluation is rewriting, see *TraceEvaluation*.

Many times though, at least for me and other simple-minded users, when
I type a builtin-function with the wrong number of parameters, it is
helpful to get an error message rather than the expression unchanged.

Generally, I do not expecting (or hope not to expect) some other clever rewrite rule,
doing strange things.

So, it is sometimes helpful when writing a builtin function,
to specify the number of parameters that are allowed, and give a standard
message when the parameter counts do not match. This can be done setting class variables
``eval_error`` and ``expected_args`` of a builtin function derived from ``Builtin``.

Here is an example:

.. code-block:: python

class Hello(Builtin):

# Set checking that the number of arguments required to exactly one.
eval_error = Builtin.generic_argument_error
expected_args = 1


Now when I call *Hello* without any arguments you will see::

In[4]:= Hello[]
Hello::argx: Hello called with 0 arguments; 1 argument is expected.

The value of ``expected_args`` does not have to be an integer. It can be a range, like:

* ``range(5)`` one to four arguments
* ``range(2,4)``: two or three arguments

or a set:

* ``{1, 3, 5}`` one, three, or five arguments
42 changes: 0 additions & 42 deletions docs/extending/developing-code/extending/tutorial/7-warnings.rst

This file was deleted.

64 changes: 64 additions & 0 deletions docs/extending/developing-code/extending/tutorial/8-warnings.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
Emitting Warnings and Errors
----------------------------

Previously, in :ref:`Checking Parameter Counts`, we showed how to give
and error when giving the wrong number of parameters for a function.

But other things can wrong, too. How can one emit an error from inside an evaluator?

Warnings and error messages in Mathics3 can be specified via the ``messages`` class field. The
``messages`` class field is a dictionary whose keys are the names of possible
warning messages and whose values are template warning messages. For example,
we may want to display a warning when our users pass something other than a
string to ``Hello``:

.. code-block:: python

from typing import Optional
from mathics.builtin.base import Builtin, String
from mathics.core.evaluation import Evaluation

class Hello(Builtin):
"""
<dl>
<dt>Hello[$person$]
<dd>An example function in a Python-importable Mathics3 module.
</dl>
>> Hello["World"]
= Hello, World!
"""

messages = {
'nstr': '`1` is not a string',
}

def eval(self, person, evaluation: Evaluation) -> Optional[String]:
"Hello[person_]"

if not isnstance(person, String):
return evaluation.message('Hello', 'nstr', person)

return String(f"Hello, {person.value}!")

In this case, calling ``Hello[45]`` will emit the warning ``nstr: 45
is not a string``. Since it is inside a Python ``return`` statement,
the code does not continue. However, the expression will be returned unevaluated.

If you want to indicate a *failure*, then return ``SymbolFailed``:


.. code-block:: python

...
from mathics.systemsymbols import SymbolFailed

class Hello(Builtin):
...
def eval(self, person, evaluation: Evaluation) -> Optional[String] | SymbolNone:
"Hello[person_]"

if not isinstance(person, String):
evaluation.message("Hello", "nstr", person)
return SymbolFailed

...