Friday, March 25, 2011

Closure in javascript and monad in python

What is a closure?

A closure is a function that returns a function parametrized by variables from lexical scope of the creator. Returned function encloses variables from outside of it's scope.

Javascript:
function createDestroyer(divId) {
    return function() {
        jQuery(divId).remove()
    }
}

var popupLayer = jQuery(document.createElement("DIV")))
popupLayer.attr("id", "popup5")
// fill popupLayer
// prepare layer closing button
var closeButton = document.createElement("A")
closeButton.addEventListener('click', createDestroyer("popup5"), false)

// other very good example is the anonymous function below:
repeats = 0
animationId = window.setInterval(function() {
    // animate something

    if (repeats++ > 100) {
        window.clearInterval(animationId); // access and capture outer scope variable
    }
}, 250)

What is a monad?

A monad is a wrapper around data type. Each function called on a monad has access to wrapped data and returns the monad.

We have just seen one monad: jQuery. It wraps around any DOM element, each call returns jQuery object (so that we can chain for example ajax().css().show()) and each method has direct access to wrapped DOM node.

Effectively every object composition with methods that return instance (this) is a monad.

Let's build one in python (IDLE session dump):
>>> class MyMonad(object):
 def __init__(self, anArray):
  self.obj = anArray

 def add(self, elem):
  self.obj.append(elem)
  return self

 def butLast(self):
  self.obj = self.obj[:-1]
  return self

 def rest(self):
  self.obj = self.obj[1:]
  return self

 def toString(self):
  print str(self.obj)
  return self

 
>>> MyMonad([1, 2, 3, 5]).butLast().toString()
[1, 2, 3]
<__main__.MyMonad object at 0x90b556c>

>>> MyMonad([1, 2, 3, 5]).add("a").toString()
[1, 2, 3, 5, 'a']
<__main__.MyMonad object at 0x90d862c>

>>> MyMonad([1, 2, 3, 5]).butLast().add("a").rest().add(90).toString()
[2, 3, 'a', 90]
<__main__.MyMonad object at 0x90d83ec>

Always returning self is so useful (especially when creating DSLs), that I came to expect this feature everywhere ;)