Monads/Writer monad

From Rosetta Code
Revision as of 23:08, 1 February 2016 by Hout (talk | contribs) (→‎ES5)


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");


   // 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);
       return {
           value: writerB.value,
           log: w.log + '\n' + writerB.log
       };
   }
   // We can compose a chain of safe 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\nobtained square root\nadded 1\ndivided by 2"}