|
|
Mathematica provides a perfect way to define monad by setting UpValues and DownValues of some symbol. Please, find specifications for monads Maybe and State below.
Monad Maybe:
DownValues[Just] = {Just[(a: Just[x_])] :> Just[x]};
UpValues[Just] =
{(expr: (op: Except[Just | List | Trace | UpValues | DownValues])[
a___, Just[b_], c___]) /;
!MatchQ[
Unevaluated[expr],
HoldPattern[If[__, __, Just[x_]] | If[__, Just[x_], __]]
] :> Just[op[a, b, c]]};
Rule from DownValues[Just] stands for monad Maybe multiplication law. That is removing of head duplicates. Rule from UpValues[Just] stands for bind operation of monad Maybe. One need to use special pre-condition for this pattern because Mathematica uses some wrapping code to convert evaluating/reducing expression in standard form by low-level call MakeBoxes. For example, let's see this wrapping code:
Hold[
If[False, 3,
With[{OutputSizeLimit`Dump`boxes$ =
Block[{$RecursionLimit = Typeset`$RecursionLimit},
MakeBoxes[Just[3], StandardForm]
]
},
OutputSizeLimit`Dump`loadSizeCountRules[];
If[TrueQ[BoxForm`SizeCount[OutputSizeLimit`Dump`boxes$, 1048576]],
OutputSizeLimit`Dump`boxes$,
OutputSizeLimit`Dump`encapsulateOutput[
Just[3],
$Line,
$SessionID,
5
]
]
],
Just[3]
]
]
That's why rule from UpValues[Just] has special pre-condition for being inside of condition expression. Now one can use symbol Just as a head for computations with exceptions:
UpValues[Nothing] = {_[___, Nothing, ___] :> Nothing};
Just[Just[123]]
(*
==> Just[123]
*)
Just[123] + Just[34] - (Just[1223]/Just[12321])*Just[N[Sqrt[123]]]
(*
==> Just[155.899]
*)
Thanks to @celtschk for great comments of this point.
Monad State:
return[x_] := State[s \[Function] {x, s}];
bind[m_State, f_] := State[r \[Function] (f[#[[1]]][#[[2]]] & @ Part[m, 1][r])];
runState[s_, State[f_]] := f[s];
For monad State I didn't use UpValues and DownValues just for similarity with Haskell notation. Now, one can define some sequential computation as State value with complex state logics as a monadic computation by using return and bind operations. Please, see an example:
computation =
Fold[bind, return[1],
Join[{a \[Function] s \[Function] {a, a + s},
b \[Function] s \[Function] {b, s + b/(3 s)},
c \[Function] s \[Function] {c, s + (s^2 + c)}},
Array[x \[Function] a \[Function] s \[Function] {a, s}, 300]
]
];
To get more effective computation one can use runState operation:
Fold[#2[#1[[1]]][#1[[2]]] &, runState[23, return[1]],
Join[{a \[Function] s \[Function] {a, a + s},
b \[Function] s \[Function] {b, s + b/(3 s)},
c \[Function] s \[Function] {c, s + (s^2 + c)}},
Array[x \[Function] a \[Function] s \[Function] {a, s}, 3000]
]
]
(*
==> {1, 3119113/5184}
*)
Conclusion:
- Ideas of rule-based programming and using
Head as type identifier allow user to express any(?) programming concept in Mathematica. For example, as it has just been shown, monads State and Maybe from Haskell;
- Using of
UpValues and DownValues for assigning rules to symbols and using of generalized operations (such as bind is) allow user to put expressions in different monadic environments.
|
|
|
answered
Apr 30 '12 at 13:03
|
|
Notation`package to implement monadic operators. When I get a chance later today, I may expand this comment to a proper answer, but I think this might get folks thinking in the meantime. – nixeagle Mar 25 '12 at 20:28