## 1 Introduction

The motivation for this work started from a
practical problem when the author was
writing a *Wolfram Language*

(WL) application that aimed to abstract away the notion of scalar valued functions for numerical computations. The aim was for the user to specify, in the WL, the number of arguments for a function, which arguments are discrete indices and which arguments are floating point numbers, for floating point arguments - the points and weights used for interpolations and integration over these arguments and finally specify which arguments can be used for purposes of code parallelization. Given this input the goal was for the application to create a

*FORTRAN 90*module and a directory with supplementary data. The user could then import this module in his or her program and work with an abstraction of the scalar valued function: store function values on different cores and handle them using different

*MPI*and

*OPENMP*threads, interpolate function values and integrate over function arguments.

The application requires further testing but is feature complete. Achieving this was possible, to a large extent, due to a choice to base the implementation around monadic types. This combined with the many powerful built in functions in the WL resulted in a very processing tool. The monadic style of programming can also be appropriate for other problems and in this paper a simple approach to include monads and the “do” notation in the WL is shown. This work was also influenced by an book by Bartosz Milewski [1] containing an excellent introduction to Category Therory.

The text is organized as follows. Section 2
introduces a categorical interpretation of the WL. Section
3
describes the implementation of monadic types and the “do” notation. Section
4
contains an examples and tips for practical implementations. Finally
section 5 contains a summary.
The notation used in WL pseudocode shown in some parts of the text uses
comments `(*...*)`

to hold additional information intended for the reader.

##
2 Categorical interpretation of the *Wolfram Language*

In order to think about the WL as a category a subset of the WL will be considered consisting of functions defined in the following manner:

where *pattern a* is a WL language pattern expression that specifies the
arguments accepted by the function *f* and *expression* is a WL
expression that can be constructed from the named parts of *pattern a*.
This is not directly enforced by the WL but ideally all possible results
of *f* match a different pattern, *pattern b*, so that:

is always true. In some places Curried functions will be used and this can be
interpreted as replacing `f[x]`

, `f[x][y]`

, …for `f`

in the pseudo code above and treating the replacement as a whole set of
functions built from the different values of `x`

, `y`

, …

It is seems natural to treat this subset of the WL as a category with morphisms
being functions (e.g. `f`

, `f[x]`

, `f[x][y]`

, …) and objects
being types defined by patterns (e.g. the set of all WL expressions that match *pattern a*
defines type *a* and the set of expressions that match *pattern b*
defines type *b*). In this context a monad is a type function that takes a
pattern that defines one type and returns a new pattern that defines another
type.

## 3 Introduction of monadic types and the “do” notation

A user can implement a monad *m* in the WL by supplying three definitions.
The first definition:

provides a pattern that matches a WL expression if it is an *m* monad for any type argument
*a*. The second definition implements the monadic *return* function:

if the type of *x* is *a* then this expression should be a
*m* monad for type argument *a*. In particular the resulting
WL expression should match `pattern[m]`

. The final definition is
an implementation of the monadic
*bind* operator for *m*:

where *ma* is an *m* monad for type argument *a* and
*aTOmb* is a function taking an element of type *a* to an
*m* monad for type argument *m*. The user does not have
to supply additional pattern constraints in `bind`

, however
it is up to the user to make sure that these definitions result in
an object that satisfies the monad laws.

Having these three definitions for monad *m*, the implementation of the
“do” notation in the WL is straightforward and consists of two parts.
The first is a recursive definition that reduces the `do[m]`

expression
by applying the user defined `bind[m]`

operator:

Here `bnd[m]`

will be replaced by the appropriate user supplied
`bind[m]`

function when the first argument matches `pattern[m]`

and the second
argument is a function, `fst`

, `snd`

are helper functions that take the left and
right expression from a `LeftArrow`

statement, and finally `chk[m]`

is a function that checks the result of `bind[m]`

against the user defined
`pattern[m]`

. Once `do[m]`

is reduced to a contain a single expression
the final expression is checked and returned:

These definitions together with the supplementary functions are provided for the readers convenience at [2]. This repository contains a small WL package and a number of examples.

## 4 Hanoi Tower example

This section takes a look at the classic Hanoi Tower puzzle example solved in [2]. The puzzle consists of three poles onto which stacks of disks with different sizes can be placed. At the beginning of the puzzle all disks are stacked on the first pole with smaller disks being placed on top of larger ones. The aim of the puzzle is to transport all disks from the first pole to the third pole in discrete steps. In each step only one disk can be taken from the top of a stack and placed on a larger disk or on an empty pole.

The puzzle is solved in the *hanoi_tower_example.nb* notebook from [2].
The notebook starts a definition
of a pattern for the Hanoi Tower puzzle:

The puzzle will be represented by a list containing three elements. Each element is a list and corresponds to a single pole of the puzzle. Elements of this list will be numbers corresponding to disc sizes. Another pattern is provided for expressions that will be used to record the moves that lead to the solution:

Moves will be two element lists, the first element (e.g. `1->2`

)
will record the number of the pole from which the disk was taken and the number
of the pole onto which the disk was dropped (e.g. `1`

, `2`

). The
second element will contain the current configuration of the puzzle.

Next three definitions are provided for the *hT* monad that will be used in
a recursive solution to the puzzle. The first definition is a pattern that will match any
WL expression that is an *hT* monad for any type argument:

Next the return function is defined as (please note that an empty list also matches `{move ...}`

):

Finaly the bind
operator for *hT* is given (please note that no additional pattern
constraints are necessary for `ma`

and `aTomb`

):

where `grab`

and `rest`

are helper functions:

and `join`

provides a lazy version of the `Join`

function:

Only one action is provided for this monad with two definitions.
The first one moves a single disk
of puzzle `tower`

from pole number `from`

to pole number `to`

:

The second one moves `n`

disks from pole number `from`

to pole number `to`

. This time the definition uses the “do” notation and recursion:

where `<-`

is the `LeftArrow`

operator entered in the WL
using `<ESC><-<ESC>`

and `other[i , j]`

returns a pole number
that is different then `i`

and `j`

. The procedure
of solving the puzzle can be read off directly from the code above.
First disks are moved from pole number `from`

to a pole that is
different from `from`

and `to`

. Next, one disk is moved from pole
number `from`

to pole number `to`

. Finaly the disks are moved
from the other pole to the `to`

pole.

A solution to the puzzle with three disks on the first pole in the initial configuration can be obtained by evaluating:

where `makeTower[3]`

prepares the initial configuration of the puzzle.
The result is:

The first element of this expression is the final configuration of the solved puzzle. The second element is a list containing the moves used to obtain the solution.

The repository [2] contains a couple more simple examples including a
*list* monad and a *maybe* monad. The reader is encouraged to download
and explore these notebooks.

## 5 Summary

The *Wolfram Language* comes with a very powerful set of built in
functions. The flexibility of this language makes it possible to program
in many different styles. Such freedom, however, can be overwhelming and
it is a good idea to conform to styles of programming that are tailored
to particular problems. Combining the monadic style of programming with
the powerful standard library of the *Wolfram Language* can be very
useful for problems that require a functional solution.

## References

- [1] “Category Theory For Programmers”, Bartosz Milewski, 2019 (https://www.blurb.com/b/9621951-category-theory-for-programmers-new-edition-hardco)
- [2] https://gitlab.com/kacpertopolnicki/wlmonad

Comments

There are no comments yet.