Implementing categories

It's about time we formally defined category theory as JavaScript objects. Categories are objects (types) and morphisms (functions that only work on those types). It's an extremely high-level, totally-declarative way to program, but it ensures that the code is extremely safe and reliable—perfect for APIs and libraries that are worried about concurrency and type safety.

First, we'll need a function that helps us create morphisms. We'll call it homoMorph() because they'll be homomorphisms. It will return a function that expects a function to be passed in and produces the composition of it, based on the inputs. The inputs are the types that the morphism accepts as input and gives as output. Just like our type signatures, that is, // morph :: num -> num -> [num], only the last one is the output.

var homoMorph = function( /* input1, input2,..., inputN, output */ ) {
  var before = checkTypes(arrayOf(func)(Array.prototype.slice.call(arguments, 0, arguments.length-1)));
  var after = func(arguments[arguments.length-1])
  return function(middle) {
    return function(args) {
      return after(middle.apply(this, before([].slice.apply(arguments))));   
    }
  }
}

// now we don't need to add type signature comments
// because now they're built right into the function declaration
add = homoMorph(num, num, num)(function(a,b){return a+b})
add(12,24); // returns 36
add('a', 'b'), // throws error
homoMorph(num, num, num)(function(a,b){
  return a+b;
})(18, 24); // returns 42

The homoMorph() function is fairly complex. It uses a closure (see Chapter 2, Fundamentals of Functional Programming) to return a function that accepts a function and checks its input and output values for type safety. And for that, it relies on a helper function: checkTypes, which is defined as follows:

var checkTypes = function( typeSafeties ) {
  arrayOf(func)(arr(typeSafeties));
  var argLength = typeSafeties.length;
  return function(args) {
    arr(args);
    if (args.length != argLength) {
      throw new TypeError('Expected '+ argLength + ' arguments'),
    }
    var results = [];
    for (var i=0; i<argLength; i++) {
      results[i] = typeSafeties[i](args[i]);   
    }
    return results;
  }
}

Now let's formally define some homomorphisms.

var lensHM = homoMorph(func, func, func)(lens);
var userNameHM = lensHM(
  function (u) {return u.getUsernameMaybe()}, // get
  function (u, v) { // set
    u.setUsername(v);
    return u.getUsernameMaybe(); 
  }
)
var strToUpperCase = homoMorph(str, str)(function(s) {
  return s.toUpperCase();
});
var morphFirstLetter = homoMorph(func, str, str)(function(f, s) {
  return f(s[0]).concat(s.slice(1));
});
var capFirstLetter = homoMorph(str, str)(function(s) {
  return morphFirstLetter(strToUpperCase, s)
});

Finally, we can bring it on home. The following example includes function composition, lenses, homomorphisms, and more.

// homomorphic lenses
var bill = new User();
userNameHM.set(bill, 'William'), // Returns: 'William'
userNameHM.get(bill); // Returns: 'William'

// compose
var capatolizedUsername = fcompose(capFirstLetter,userNameHM.get);
capatolizedUsername(bill, 'bill'), // Returns: 'Bill'

// it's a good idea to use homoMorph on .set and .get too
var getUserName = homoMorph(obj, str)(userNameHM.get);
var setUserName = homoMorph(obj, str, str)(userNameHM.set);
getUserName(bill); // Returns: 'Bill'
setUserName(bill, 'Billy'), // Returns: 'Billy'

// now we can rewrite capatolizeUsername with the new setter
capatolizedUsername = fcompose(capFirstLetter, setUserName);
capatolizedUsername(bill, 'will'), // Returns: 'Will'
getUserName(bill); // Returns: 'will'

The preceding code is extremely declarative, safe, reliable, and dependable.

Note

What does it mean for code to be declarative? In imperative programming, we write sequences of instructions that tell the machine how to do what we want. In functional programming, we describe relationships between values that tell the machine what we want it to compute, and the machine figures out the instruction sequences to make it happen. Functional programming is declarative.

Entire libraries and APIs can be constructed this way that allow programmers to write code freely without worrying about concurrency and type safety because those worries are handled in the backend.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.151.45