Monads/Writer monad: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎ES5: updated writer monad to include intermediate values in log)
Line 17: Line 17:
<lang JavaScript>(function () {
<lang JavaScript>(function () {
'use strict';
'use strict';

// START WITH THREE SIMPLE FUNCTIONS
// START WITH THREE SIMPLE FUNCTIONS

// Square root of a number more than 0
// Square root of a number more than 0
function root(x) {
function root(x) {
return Math.sqrt(x);
return Math.sqrt(x);
}
}

// Add 1
// Add 1
function addOne(x) {
function addOne(x) {
return x + 1;
return x + 1;
}
}

// Divide by 2
// Divide by 2
function half(x) {
function half(x) {
return x / 2;
return x / 2;
}
}


// DERIVE LOGGING VERSIONS OF EACH FUNCTION
// DERIVE LOGGING VERSIONS OF EACH FUNCTION

function loggingVersion(f, strLog) {
function loggingVersion(f, strLog) {
return function (v) {
return function (v) {
Line 46: Line 46:
}
}
}
}

var log_root = loggingVersion(root, "obtained square root"),
var log_root = loggingVersion(root, "obtained square root"),

log_addOne = loggingVersion(addOne, "added 1"),
log_addOne = loggingVersion(addOne, "added 1"),

log_half = loggingVersion(half, "divided by 2");
log_half = loggingVersion(half, "divided by 2");



// UNIT/RETURN and BIND for the the WRITER MONAD
// UNIT/RETURN and BIND for the the WRITER MONAD

// The Unit / Return function for the Writer monad:
// The Unit / Return function for the Writer monad:
// 'Lifts' a raw value into the wrapped form
// 'Lifts' a raw value into the wrapped form
Line 66: Line 66:
};
};
}
}

// The Bind function for the Writer monad:
// The Bind function for the Writer monad:
// applies a logging version of a function
// applies a logging version of a function
// to the contents of a wrapped value
// to the contents of a wrapped value
// and return a wrapped result (with extended log)
// and return a wrapped result (with extended log)

// Writer a -> (a -> Writer b) -> Writer b
// Writer a -> (a -> Writer b) -> Writer b
function writerBind(w, f) {
function writerBind(w, f) {
var writerB = f(w.value);
var writerB = f(w.value),
v = writerB.value;

return {
return {
value: writerB.value,
value: v,
log: w.log + '\n' + writerB.log
log: w.log + '\n' + writerB.log + ' -> ' + JSON.stringify(v)
};
};
}
}

// USING UNIT AND BIND TO COMPOSE LOGGING FUNCTIONS
// USING UNIT AND BIND TO COMPOSE LOGGING FUNCTIONS

// We can compose a chain of Writer functions (of any length) with a simple foldr/reduceRight
// We can compose a chain of Writer functions (of any length) with a simple foldr/reduceRight
// which starts by 'lifting' the initial value into a Writer wrapping,
// which starts by 'lifting' the initial value into a Writer wrapping,
Line 93: Line 94:
}, writerUnit(value));
}, writerUnit(value));
}
}

var half_of_addOne_of_root = function (v) {
var half_of_addOne_of_root = function (v) {
return logCompose(
return logCompose(
Line 99: Line 100:
);
);
};
};

return half_of_addOne_of_root(5);
return half_of_addOne_of_root(5);
})();</lang>
})();</lang>
Line 105: Line 106:
{{Out}}
{{Out}}


<pre>{
<pre>{"value":1.618033988749895, "log":"Initial value: 5\nobtained square root\nadded 1\ndivided by 2"}</pre>
"value":1.618033988749895,
"log":"Initial value: 5\n
obtained square root -> 2.23606797749979\n
added 1 -> 3.23606797749979\n
divided by 2 -> 1.618033988749895"
}</pre>

Revision as of 19:39, 2 February 2016


The Writer monad is a coding pattern which allows composition of functions which return their results combined with a log string. The final result of a composed function yields both the resulting value, and a concatenation of the logs from each component function application.

Demonstrate in your programming language the following:

  1. Construct a Writer monad by writing the 'bind' function and the 'unit' (sometimes known as 'return') function for that monad (or just use what the language already provides)
  2. Write three simple functions: root, addOne, and half
  3. Derive Writer monad versions of each of these functions
  4. Apply a composition of the Writer versions of root, addOne, and half to the integer 5, deriving both a value for the Golden Ratio φ, and a concatenated log of the function applications (starting with the initial value, and followed by the application of root, etc.)


JavaScript

ES5

<lang JavaScript>(function () {

   'use strict';

   // START WITH THREE SIMPLE FUNCTIONS

   // Square root of a number more than 0
   function root(x) {
       return Math.sqrt(x);
   }

   // Add 1
   function addOne(x) {
       return x + 1;
   }

   // Divide by 2
   function half(x) {
       return x / 2;
   }


   // DERIVE LOGGING VERSIONS OF EACH FUNCTION

   function loggingVersion(f, strLog) {
       return function (v) {
           return {
               value: f(v),
               log: strLog
           };
       }
   }

   var log_root = loggingVersion(root, "obtained square root"),

       log_addOne = loggingVersion(addOne, "added 1"),

       log_half = loggingVersion(half, "divided by 2");



   // UNIT/RETURN and BIND for the the WRITER MONAD

   // The Unit / Return function for the Writer monad:
   // 'Lifts' a raw value into the wrapped form
   // a -> Writer a
   function writerUnit(a) {
       return {
           value: a,
           log: "Initial value: " + JSON.stringify(a)
       };
   }

   // The Bind function for the Writer monad:
   // applies a logging version of a function
   // to the contents of a wrapped value
   // and return a wrapped result (with extended log)

   // Writer a -> (a -> Writer b) -> Writer b
   function writerBind(w, f) {
       var writerB = f(w.value),

v = writerB.value;

       return {
           value: v,
           log: w.log + '\n' + writerB.log + ' -> ' + JSON.stringify(v)
       };
   }

   // USING UNIT AND BIND TO COMPOSE LOGGING FUNCTIONS

   // We can compose a chain of Writer functions (of any length) with a simple foldr/reduceRight
   // which starts by 'lifting' the initial value into a Writer wrapping,
   // and then nests function applications (working from right to left)
   function logCompose(lstFunctions, value) {
       return lstFunctions
           .reduceRight(function (writerA, f) {
               return writerBind(writerA, f);
           }, writerUnit(value));
   }

   var half_of_addOne_of_root = function (v) {
       return logCompose(
           [log_half, log_addOne, log_root], v
       );
   };

   return half_of_addOne_of_root(5);

})();</lang>

Output:
{
    "value":1.618033988749895,
    "log":"Initial value: 5\n
           obtained square root -> 2.23606797749979\n
           added 1 -> 3.23606797749979\n
           divided by 2 -> 1.618033988749895"
}