Disfunctional RSS

Where misquotes, misinformation, and misspellings occur daily.

Archive

May
12th
Sat
permalink

Tweet Grammer Fixer: OO vs FP

To play with what I’ve been learning, I’m going to make a OO vs Functional comparison. The goal is to clean up tweet grammer. The output should be something like:
// Before
// "I'm tweeting like an animal but I keep missing punctuation. someone make me a tool.Then I can tweet like a scholar."
// After
// "I'm tweeting like an animal, but I keep missing punctuation. Someone make me a tool. Then I can tweet like a scholar."
Object Oriented Functional
var GrammerChecker = function(tweets) {
  this.tweets = tweets;
}

GrammerChecker.prototype = {
  check: function() {
    return this.tweets.map(function(t){
      var clean_tweet = new CleanTweet(t);
      return clean_tweet.clean();
    });
  }
}


var CleanTweet = function(tweet) {
  this.tweet = tweet;
}

CleanTweet.prototype = {
  clean: function() {
    this.commaBeforeBut();
    this.capitolAfterPeriod();
    this.spaceAfterPeriod();
    return this.tweet;
  },
  
  capitolAfterPeriod: function() {
    this.tweet.replace(/\.\s+([a-z])/g, function(word){ return word.toUpperCase(); });
  },
  
  spaceAfterPeriod: function() {
    this.tweet.replace(/(\w)\.(\w)/g, "$1. $2");
  },
  
  commaBeforeBut: function() {
    this.tweet.replace(/(\w+)[^,]but/gi, "$1, but");
  }
}

module.exports = GrammerChecker;

commaBeforeBut = replace(/(\w+)[^,]but/gi, "$1, but");

capitolAfterPeriod = replace(/\.\s+([a-z])/g, '.toUpperCase()'.lambda());

spaceAfterPeriod = replace(/(\w)\.(\w)/g, "$1. $2");

check = map(compose(spaceAfterPeriod, capitolAfterPeriod, commaBeforeBut));

module.exports = {check: check};

And here’s using it:
Object Oriented Functional
var requester = require("../requester1");
var GrammerChecker = require("./grammer_check_oo");


requester.get("http://search.twitter.com/search.json", {q: "food"}, function(response){
  var json = JSON.parse(response);
  var tweets = json.results.map(function(r){ return r.text; });
  var checker = new GrammerChecker(tweets);
  var result = checker.check();
  console.log(result);
});

require('../functional');
var requester = require("../requester");
var GrammerCheck = require("./grammer_check_fun");

transform = compose(map('.text'), '.results', JSON.parse);
getTweets = requester.get("http://search.twitter.com/search.json", {q: "food"});
getTweets(compose(log, GrammerCheck.check, transform));

To get this haskelly in javascript, I’m using osteele’s functional.js and a few function wrappers of my own. The functional version of replace auto-currys and doesn’t destroy the original string. The helper libraries are Here

What i noticed

After writing the example, I realized that the OO version wasn’t very resuable due to my nomenclature. If I rename “tweet” to “sentence”, i get a little library out of it. I often try to rename things afterwards. This plays a large part in cleaning up my code - generalize and extract everything possible into a lib, then implement with specific verbage in the app. But for the functional side, due to pointfree style, I don’t need to name anything anyways! Done. In fact, the functional side looks more like a libary than app code. It’s natural to write this way. We could extend it pretty easily into functions that make dsl-like grammer checker.
commaBefore = function(word) {
  return replace(new RegExp('(\\w+)[^,]'+word, 'gi'), "$1, "+word);
}
Now we can do things like this:
Object Oriented Functional
// chaining by returning 'this'
var checker = new GrammerChecker(tweets);
checker.commaBefore("but").commaBefore("however").commaBefore("yet").result;

map(compose(commaBefore("but"), commaBefore("yet"), commaBefore("however")));

But then when I want to say, compose the GrammerChecker with a HtmlEntities sanitizer so I can switch stuff like &quote; to “, the result would probably be as follows:
Object Oriented Functional
var checker = new GrammerChecker(tweets);
var cleaned_grammer = checker.commaBeforeBut("but").commaBefore("however").commaBefore("yet").result;
cleaned_grammer.map(function(cg){
	var entities = new HtmlEntities(cg);
	return entities.santize();
});

var check = compose(commaBefore("but"), commaBefore("yet"), commaBefore("however"));
var getResult = map(compose(HtmlEntities.santize, check));
getResult(tweets);

For OO, I have to create a new object, put the data in it so i can do stuff to that data, then do that stuff. In FP, since we can compose functions or even programs together, we can just glue them both together and pipe our data through it.

So who wins?

Which is better? I’ll leave that to the reader to decide, but I do have some things to note.
  • Functional is a fraction of the size of code. Distilled to just parts that matter. OO requires a lot more ceremony to get started.
  • OO would be tricky to make parellel due to imperative instruction and destructive methods.
  • OO is rather black boxy during usage. You see the tweets go in to the GrammerChecker, but don’t know what’s going on from there. Then the check() method returns a result, which are hopefully the cleaned tweets, but it’s not that obvious (probably my fault). This may seem kind of trivial here, but i think it’s a huge deal when working with more complex examples. With Functional, you know the check function can only take an argument and return a result (as all functions in the paradigm do) and there’s only 1 function call (no constructor) so it’s pretty easy to see where the tweets go.
  1. liamgoodacre reblogged this from drboolean
blog comments powered by Disqus