Monads/Writer monad: Difference between revisions

From Rosetta Code
Content added Content deleted
No edit summary
No edit summary
Line 1: Line 1:
[[Category:Monads]]
[[Category:Monads]]


The Writer monad is a coding design pattern which which makes it possible to compose 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.
The Writer monad is a coding design pattern which makes it possible to compose 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:
Demonstrate in your programming language the following:

Revision as of 19:03, 6 February 2016


The Writer monad is a coding design pattern which makes it possible to compose 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.)

EchoLisp

Our monadic Writer elements will be pairs (string . value), where string is the log string.

<lang scheme> (define (Writer.unit x (log #f)) (if log (cons log x) (cons (format "init → %d" x) x)))

f is a lisp function
(Writer.lift f) returns a Writer function which returns a Writer element

(define (Writer.lift f name) (lambda(elem)

            (Writer.unit 
            	(f (rest elem)) 
               (format "%a \n %a  → %a" (first elem) name (f (rest elem))))))
                           
lifts and applies

(define (Writer.bind f elem) ((Writer.lift f (string f)) elem))

(define (Writer.print elem) (writeln 'result (rest elem)) (writeln (first elem)))

Writer monad versions

(define w-root (Writer.lift sqrt "root")) (define w-half (Writer.lift (lambda(x) (// x 2)) "half")) (define w-inc ( Writer.lift add1 "add-one"))


no binding required, as we use Writer lifted functions

(-> 5 Writer.unit w-root w-inc w-half Writer.print)

result 1.618033988749895 init → 5 root → 2.23606797749979 add-one → 3.23606797749979 half → 1.618033988749895

binding

(->> 0 Writer.unit (Writer.bind sin) (Writer.bind cos) w-inc w-half Writer.print)

result 1 init → 0 sin → 0 cos → 1 add-one → 2 half → 1 </lang>


J

Based on javascript implementation:

<lang J>root=: %: incr=: >: half=: -:

tostr=: ,@":

loggingVersion=: conjunction define

 n;~u

)

Lroot=: root loggingVersion 'obtained square root' Lincr=: incr loggingVersion 'added 1' Lhalf=: half loggingVersion 'divided by 2'

loggingUnit=: verb define

 y;'Initial value: ',tostr y

)

loggingBind=: adverb define

 r=. u 0{::y
 v=. 0{:: r
 v;(1{::y),LF,(1{::r),' -> ',tostr v 

)

loggingCompose=: dyad define

 ;(dyad def '<x`:6 loggingBind;y')/x,<loggingUnit y

)</lang>

Task example:

<lang J> 0{::Lhalf`Lincr`Lroot loggingCompose 5 1.61803

  1{::Lhalf`Lincr`Lroot loggingCompose 5

Initial value: 5 obtained square root -> 2.23607 added 1 -> 3.23607 divided by 2 -> 1.61803</lang>

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(
           writerBind,
           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"
}

zkl

Translation of: EchoLisp

<lang zkl>class Writer{

  fcn init(x){ var X=x, logText=Data(Void,"  init \U2192; ",x.toString()) }
  fcn unit(text)  { logText.append(text); self }
  fcn lift(f,name){ unit("\n  %s \U2192; %s".fmt(name,X=f(X))) }
  fcn bind(f,name){ lift.fp(f,name) }
  fcn toString{ "Result = %s\n%s".fmt(X,logText.text) }
  fcn root{ lift(fcn(x){ x.sqrt() },"root") }
  fcn half{ lift('/(2),"half") }
  fcn inc { lift('+(1),"inc") }

}</lang> <lang zkl>Writer(5.0).root().inc().half().println();

w:=Writer(5.0); Utils.Helpers.fcomp(w.half,w.inc,w.root)(w).println();</lang> Use bind to add functions to an existing Writer: <lang zkl>w:=Writer(5.0); root,inc,half := w.bind(fcn(x){ x.sqrt() },"root"), w.bind('+(1),"+ 1"), w.bind('/(2),"/ 2"); root(); inc(); half(); w.println();</lang>

Output:
Result = 1.61803
  init → 5
  root → 2.23607
  inc → 3.23607
  half → 1.61803
Result = 1.61803
  init → 5
  root → 2.23607
  inc → 3.23607
  half → 1.61803
Result = 1.61803
  init → 5
  root → 2.23607
  + 1 → 3.23607
  / 2 → 1.61803