I have been trying to code monad-like structures in Mathematica. However, I am facing some issues and I was wondering if anyone can help. I will give some background in the hope it can be useful to others. Also, while there may be a native way to perform some of these tasks in Mathematica and I would like also to know it, I am more interested in how to code them.
First, let's look at a Haskel-like Maybe monad. The idea is that some computations can fail and return nothing
instead of a valid result. For example, consider the safe division:
safedivide[a_, b_] := If[b == 0, nth[a], a/b];
nth[_] := nothing
(Here the function nth[_] serves to fix a problem with the upvalues of nothing defined later, otherwise safedivision will return nothing in all cases).
Thus,
safedivide[1,0]
returns nothing
whereas
safedivide[0,1]
returns 0
. Now, we would like to be able to use nothing
as the argument of a function so that if one of the intermediate steps returns nothing
then the whole computation returns nothing
. This can be accomplished defining UpValues
for nothing
as follows:
nothing/:f_[___,nothing,___]=nothing
Accordingly, if we attempt to compute
Sin[safedivide[1,0]+3]
we obtain nothing
whereas Sin[safedivide[0,1]+3]
gives Sin[3]
. This construction works well, at least as far as I can see, with built-in function. For example
Max[{1, 2, 3, nothing, 5}]
returns nothing
.
So far so good, and now I got really ambitious and decided to create a comment
wrapper. The idea is that functions can return their usual outputs or return them wrapped in a comment. For example,
commentedsquare[x_] := If[x == 0, comment[0, "Zero"], x^2]
Thus, commentedsquare[0]
returns comment[0, "Zero"]
. We want to pass through all these comments to various functions and collect them at the end. I understand that this can be achieved by the Reap
-Sow
construct, for example as in
commentedsquare2[x_] := If[x == 0, Sow["Zero"]; 0, x^2]
Reap[commentedsquare2[0]]
However, for the sake of generality, I would like a construction that would work as follows: for a generic function f
:
f[comment[x,"abc"], comment[y,"def"], z]=comment[f[x,y,z], "abc","def"]
That is, the comments would be passed through the function and the function evaluates at the first argument of comment
.
I managed to code this for functions of a single variable as follows:
notcomment=(!(#===comment)&);
comment /: f_?notcomment[comment[a_, b___]] := comment[f[a], b];
comment[comment[a_, b___], c___]:= comment[a, b, c];
comment[a_] := a;
For example,
comment[comment[1, "abc"], "def"]
returns comment[1, "abc", "def"]
and
N[comment[Cos[Sin[comment[comment[Sin[comment[comment[1]]], "abc"], "def"]]]]]
returns, as it should, comment[0.734665, "abc", "def"]
.
Now, my main issue is to extend this to functions of several variables. With the preceding code
Sin[comment[Pi, "abc"]] + 1
returns 1 + comment[0, "abc"]
, and so I have attempted to code something like:
comment/:f_?notcomment[a___,comment[b_,c___],d___]:=comment[f[a,b,d],c];
which has problems with recursion and simply does not work. I really tried several alternatives but nothing seems to do the right job.
Any ideas?