import pymonad as pm;
from math import pi,sin,sqrt;

#------------------------------------------------------------------------------#

def fFormat(x):
   return ':{0:.3f}'.format(x);

def output(tpl):
   (v,s)=tpl;
   if s=='': s+='<empty>';
   print('y'+fFormat(v)+' # '+s,'\n');
# end def

def toTuple(w):
   return (w.getResult(),w.getLog());

#------------------------------------------------------------------------------#

@pm.curry
def fAdd(x1,x2):
   y=x1+x2;
   return y;

@pm.curry
def fMul(x1,x2):
   y=x1*x2;
   return y;

#------------------------------------------------------------------------------#

@pm.curry
def wSin(x):
   y=sin(x);
   return pm.StringWriter(y,'|fSin'+fFormat(y));

@pm.curry
def wSqrt(x): # Pre: x>=0
   y=sqrt(x);
   return pm.StringWriter(y,'|fSqrt'+fFormat(y));

@pm.curry
def wAdd(x1,x2):
   y=x1+x2;
   return pm.StringWriter(y,'|fAdd'+fFormat(y));

@pm.curry
def wMul(x1,x2):
   y=x1*x2;
   return pm.StringWriter(y,'|fMul'+fFormat(y));

#------------------------------------------------------------------------------#

def wLift(f):
   @pm.curry
   def lift(x):
       v=f(x);            
       s='|'+f.__name__+fFormat(v);
       return pm.StringWriter(v,s);
   return lift;
# end def

@wLift
def wSquare(x):
   return x*x;

#------------------------------------------------------------------------------#

if False:
   y=fAdd(2,3); print(y);
   fAdd3=fAdd(3);
   y=fAdd3(2); print(y);

   # * is the composition operator for functions
   #
   comp=fAdd(2) * fAdd(3);
   y=comp(5); print(y); # ((5+2)+3)=10

#------------------------------------------------------------------------------#

# >> is the bind operator of the monad

x1=pi/4; x2=1;

y=pm.unit(pm.StringWriter,x1) >> wSin >> wAdd(x2) >> wSqrt >> wSquare;

res=toTuple(y);
if False:
   output(res);
   #>>> y:1.707 # |fSin:0.707|fAdd:1.707|fSqrt:1.307|fSquare:1.707


x1=2; x2=3; x3=4; x4=5;

y=pm.unit(pm.StringWriter,x1) >> wMul(x2) >> wAdd(x3) >> wMul(x4);

res=toTuple(y);
if False:
   output(res);
   #>>> y:50.000 # |fMul:6.000|fAdd:10.000|fMul:50.000

#------------------------------------------------------------------------------#