Introduction

I have been learning Haskell recently, and I decided to see if I could implement monads in the Wolfram language. To test this, I’ve also implemented a version of Haskell’s “do” notation, and the “state” monad.

Issues

One issue I ran into is that the Wolfram Language does this:
Function
In[]:=
foo[f_]:=x|->f[x]
In[]:=
Nest[foo,b,3]
Out[]=
Function[x$,Function[x$,Function[x$,b[x$]][x$]][x$]]
Which ended up causing problems for my function when people used the “” syntax for defining functions in their monads. One can get around this by defining foo equivalently as
In[]:=
foo[f_]:=f[#]&
In[]:=
Nest[foo,b,3]
Out[]=
((b[#1]&)[#1]&)[#1]&
(the answer I wanted) but I want the user of the monads to not have to worry about what specific syntax they use for defining functions, and that they might get the wrong answer if they use the “wrong” syntax. So Kurt Gimbel helped me with that part, and did almost all of the design of the “uniquizeVars” function which makes sure that nested functions always have unique variables when they are supposed to be unique. If one avoids using the “” or Function syntax with named variables, one can just do away with that function, and the definition for the monads would look a bit more elegant. But I wanted that feature so it’s here.
​
Below I define the code for the “do” notation and the “state” monad, and then set up and run some composed stateful computations involving pushing and popping, with the defined “do” notation.

Do Notation Code

In[]:=
then[x_,y_]:=bind[x,y&]
In[]:=
myValueQ[expr_]:=ValueQ[expr,Method->"SymbolDefinitionsPresent"]||Context[expr]==="System`"
In[]:=
uniqueizeVars[expr_]:=Module[{},​​expr/.​​(Symbol[#]:>​​With[{eval=Symbol[#<>ToString[$ModuleNumber]]},​​eval/;True]&/@​​Cases[expr,​​sym_Symbol?(Not@*myValueQ):>​​SymbolName@Unevaluated[sym],​​{0,Infinity}])]
In[]:=
mDoPart[get[x_,y_],z_]:=bind[y,x|->z]
In[]:=
mDoPart[x_,y_]:=then[x,y]
In[]:=
mDo[x__]:=Fold[uniqueizeVars@mDoPart[#2,#1]&,Reverse[{x}]]

State Monad Code

In[]:=
bind[state[h_],f_]:=​​state[s|->​​Replace[h[s],​​{a_,newState_}:>​​Replace[f[a],​​state[x_]:>x[newState]]]]
In[]:=
return[x_]:=state[{x,#}&]
In[]:=
runState[state[computation_],myState_List]:=computation[myState]

Push and Pop Definitions

In[]:=
pushI[x_][{xs___}]:={{},{x,xs}}
In[]:=
push=state@*pushI;
In[]:=
popI[{x_,xs___}]:={x,{xs}}
In[]:=
pop=state[popI];

Stack Manipulation Setup

In[]:=
stackManip=​​mDo[​​push[3],​​pop,​​pop];
In[]:=
stackStuff=​​mDo[​​get[a,pop],​​If[a==5,​​push[5]​​,​​mDo[​​push[3],​​push[8]]]​​];
In[]:=
moreStack=​​mDo[​​get[a,stackManip]​​,​​If[a==100,​​stackStuff,​​return[{}]]];

Calculations

In[]:=
runState[moreStack,{100,0,2,1,0}]
Out[]=
{{},{8,3,2,1,0}}
In[]:=
runState[moreStack,{100,5,2,1,0}]
Out[]=
{{},{5,2,1,0}}
In[]:=
runState[moreStack,{5,0,2,1,0}]
Out[]=
{{},{0,2,1,0}}
In[]:=
runState[moreStack,{5,5,2,1,0}]
Out[]=
{{},{5,2,1,0}}

CITE THIS NOTEBOOK

Implementing Haskell's state monad and do notation in Wolfram​
by Eric Parfitt​
Wolfram Community, STAFF PICKS, June 13, 2025
​https://community.wolfram.com/groups/-/m/t/3478726