Refactoring with Functional Programming Like a Boss in Javascript

An example case for refactoring with javascript with functional programming.

Let’s consider an issue that recently came up for a game I was working on. I wanted to effectively create a gradient across the time dimension (i.e. that is a smooth color transition over time). This can create a flashing effect or be used for a number of other cases. In order to make this versatile I decided I wanted a function that would take two colors, and a float (0 to 1) that would represent how much of color2 should be used in the mixture (the rest would be color 1). Thus at partial_mix(c1, c2, 0) we should get just c1, and at partial_mix(c1, c2, 1) we should get just c2.

A sample solution ***BAD CODE***:


/**
* A bad solution to this problem
*/
partial_mix: function(c1, c2, pct_b) {
var red1, blue1, green1, red2, blue2, green2; 
red1 = parseInt(c1.substr(0,2));
green1 = parseInt(c1.substr(2,2));
blue1 = parseInt(c1.substr(4,2));

red2 = parseInt(c2.substr(0,2));
green2 = parseInt(c2.substr(2,2));
blue2 = parseInt(c2.substr(4,2));

var mixture = mix(red1, red2, pct_b) + mix(green1, green2, pct_b) + mix(blue1, blue2, pct_b);
}

function mix(n1, n2, pct_b){
return (n1 + (n2 - n1) * pct_b).to2dhex();
}

Problem 1: Not dry across Red, Green, and Blue. We did the exact same thing to red, green, and blue, yet we copy pasted to handle the redundancy. Not good. Better to use arrays (with the added benefit of easing the process of adding an alpha color later):


var x, color1 = [], color2 = [], result=[];
for (x=0; x < 3; x++) {
color1[x] = parseInt(c1.substr(2*x, 2*(x+1)), 16 );
color2[x] = parseInt(c2.substr(2*x, 2*(x+1)), 16) ;
result[x] = mix(color1[x], color2[x], pct_b);
}
return result.join("");

Problem 2: Code assumes 6 digit format. What if we want to be able to pass 3 character formats too (for either, neither, or both parameters)?

It should be apparent pretty quickly that it’s not pretty to make the code we have versatile in this respect. Instead let’s regularize our input so that it always is 6 digit then leave our code intact:


var doubleString = function(str) { return "" + str + str;}; 
var toSixDigitColor = function(color) { 
    if (color.length < 6) {
      return color.split("").map(doubleString).join("");
    }
   return color;
}

c1 = toSixDigitColor(c1);
c2 = toSixDigitColor(c2);
.
.
.
}

Problem 3: Still duplicates the code performed on color1 and color2:

mix_colors: function(c1, c2, pct_b) {
var mix = function (c1, c2) { ... };
var doubleString = function (str) {...}; 
var toSixDigitColor = function (color) {...};

var shades = [c1,c2].map(function(color) {
color = toSixDigitColor(color);
return color.split("").chunk(2).map(function(a) { return parseInt(a.join(""),16); }); /* convert to an array of 3 values */
});

return _.zip(shades[0],shades[1]).map2red(mix).join("");
}

And that’s what I call leveraging functional programming in javascript. We know it’s well-written code because when we ask what we have to do to add an alpha color, we realize the answer is nothing, it’s versatile enough to do it automatically (even when we combine the requirements of allowing single-digit colors). Notice I pull in underscore for zip, and write two custom functions: map2red and chunk.


Number.prototype.to2dhex = function() { return ("0" + parseInt(this).toString(16)).substr(-2); };

// More on a more general solution to this later
Array.prototype.map2red = function(func) { return this.map(function(v) { return v.reduce(func); }) };

// Breaks an array into sub arrays of length = size
//this is begging to be rewritten as a right apply on divide composed with parseInt passed to _.groupBy
Array.prototype.chunk = function(size) { 
var tmp = [], x; 
for (x=0; x < this.length; x++) {
  tmp[parseInt(x/size)] = tmp[parseInt(x/size)] || []; 
  tmp[parseInt(x/size)].push(this[x]);
} 
return tmp; 
};

Thoughts on the equation of automated testing

Like any other principle, types of testing justify their existence in the time they save you. That is, presuming that software quality is ensured by manual testing in the absence of automated testing, the criteria for telling if adding a test is appropriate is whether the time taken to write the test is less than the expected time saved by the test.

I want to touch on the notion of testing everything. Aside from being inefficient, I’m confident it’s not realistically possible. That is: I’d wager that for any real, used codebase with a set of tests one can write an alteration that will break the codebase while keeping all the tests passing.

If you accept the conclusion that testing everything isn’t plausible (and no, 100% code coverage certainly isn’t testing everything), then the question becomes when/where do we test? Well, obviously in cases when the time saved exceeds the time spent implementing the tests. But let’s break this formula down a little. What factors influence the time saved and inform the decision of which things to test?

  1. The probability that code will break (and the number of times)
  2. The difficulty of manual testing
  3. The probability that the test will catch the code breaking

This is a start, let’s break it down further:

  1. The probability that code will break (and the number of times)
    • Amount of change to be made to code
    • Difficulty of code
    • Team Size (i.e. people working on code who don’t understand it)
    • Intensity of external factors/dependencies (environments)
  2. The difficulty of manual testing
    • The variety of different cases in the piece of code (“branchiness”)
    • The visibility of a problem when present
  3. The probability that the test will catch the code breaking

Based on this list, a hypothetical case where implementing a unit test might be the most valuable:  You have a complicated algorithm that uses advanced mathematics you don’t entirely understand which you will need to optimize several times for speed. Moreover, other team members will be working on it too. It relies on special GPU processing which has varying logic for different hardware. It also has branching logic that requires at least 8 test cases to ensure accuracy. Because the math is so complex, determining if the function has answered correctly requires looking at every digit of a 15 digit number.

A hypothetical case where implementing a unit test might be the least valuable:  You are putting one line of code into your app’s startup to set the background color to steel-grey. It’s a basic 1-liner. Nobody else will touch your code, and you know for a fact from your waterfall documentation that this requirement will never be altered. Your app only runs on a very specific device on one OS. There is no special logic involved. Every time you startup your app (which takes .01 seconds) you’ll immediately notice whether or not the background color is right. 

I believe an expert engineer will always be doing this math with all code he writes, while keeping in mind a margin of error in his estimations. And I think any engineer who opts for a simpler equation is being simplistic and in that respect, the less advanced engineer.

The [limited] Role of Principles

A programming principle should only be followed when it provides more long-term benefit than any alternative. This may seem self-evident; it should be apparent from the fact that a principle that doesn’t provide long-term benefit obviously isn’t a good principle.

This is something that mediocre problem-solvers don’t always understand: deviation from generally accepted principle can be a sign of weakness or, contrarily, a sign of strength.

To take an analogy, in chess it is said that a rook is worth five, a bishop three, and a pawn one and so on. These facts aren’t anywhere in the rules of chess, they are just very good approximations, overall, that emerge from the game. About 90% of the time, you can use math based on these numbers to make good decisions.

However, 10% of the time other situation factors supersede this principle. Though there is an appeal to such simplicity, as one becomes closer to a master of chess he must let go of these numbers and move to a more advanced situation calculus. The same applies to coding.

Even the purest of coding principles have exceptions: though you’ve probably never considered it, every time you copy-paste you participate in a violation of DRY.

I’d like to reiterate, every single (non-trivial) coding principle necessarily has an exception. Why? Because every principle incurs a cost of a situation benefit. And for any such principle we can imagine a scenario, however unlikely, where the situationality of that benefit is totally avoided.

There is only one universal principle of problem-solving: the cost-benefit analysis. Every other principles only offer value as heuristic approximations. This is the calculus by which an expert must weigh the relevance and generality of any other principle.

A UI Case study: Chrome Developer Tools

(All pictures taken at the time of writing, no doubt features and interface will change with time)

For this case study I’ll pick on chrome because it’s hands-down my favorite browser, and because I want to elaborate on the criticism I made in my UI manifesto. It’s excellent in many ways, particularly the developer tools. However, I periodically still learn about cool hidden features it has.

Case in point: http://www.igvita.com/slides/2012/devtools-tips-and-tricks/ . It’s a mixed compliment to have an article written about your software that entitled, “Wait, [your software] can do that?” It’s great to impress with your feature set, but the fact that such an article needs to be written to unearth these capabilities is indicative of underlying unintuitive UI.

My thoughts below:

How chrome dev tools looked at the time this post was authored.

 

My Complaints

 

A mockup of a redesign of chrome dev tools UI that fixes many of the issues

 

UI / UX Manifesto (part 1)

Every single application I use fails UI/UX in simple, fixable ways.

Part 1: An Analogy 

You travel 200 years into the future. And walk into a bank. Fortunately everything is in english. You recall having some savings that should have accumulated some interest by now, so you wan to withdraw. The first thing you do is look around the bank nothing is familiar 1. So you look for somebody to ask for help, though there are people, you don’t know who is an employee and who is not an employee since there are no consistent uniforms and there is no “counter” that would designate employee-only zone 2. You walk up to a girl in grey and ask “Hi can you help me, I’m quite new here and am totally lost!”

The attractive young girl looks back and says, “Open all bookmarked accounts or Open all bookmarked accounts in table view?” You look at her blankly and she returns the blank stare before repeating herself exactly.

You realize she is an android. “Help?” You ask her “Can I speak to your supervisor?” Nothing. You realize she is unable to communicate with you and also unable to help you find anybody who can 3. You decide you need to leave the bank and get help from outside. As you start to walk out the door, a guard blocks your path and asks, “Are you sure you want to terminate transaction initiation?” You have no idea what this is but you’re uncomfortable and say yes and hustle away.

Perhaps you come away blaming yourself for not understanding. Even though you know that with time you could become an expert in such a place, you leave frustrated and preferring not to return.

Part 2: The Alternative

You walk into a futuristic bank. Their advanced technology recognizes you have never been here before, but doesn’t aggressively nag you about it. You see several clearly distinct counters, each with one person in a special red outfit, you infer those are the employees. You walk up to the nearest station and say “Hi, I’m new here I need to check if I have an account.” Though full language comprehension technology isn’t around, the android recognizes you are a new person and that what you said was very far from an appropriate action. “I didn’t understand that. I am an automated account-opener robot. I recognize the commands ‘open account’, ‘close account’, and ‘help’. You can also always find help at our help desk.” She gestures.

You see that one of the stations had a HUGE sign labeled HELP hanging over it, and every desk has a sign over it4. You feel a bit silly for not noticing it sooner. You go to the help desk and soon are getting futuristic video tutorials.

Part 3: The Lessons

  1. Follow a convention for design (preferably a common one).

    This is actually a point I have seen championed before in UI, yet it just doesn’t seem to be followed. It’s like going into a house and not realizing there was a door somewhere because the nob was so elaborate (or out-of-place) it was unrecognizable.

    Chrome failing to follow any convention in its UI

    I’m going to pick on chrome because it’s one of my favorite applications. Look at the debug toolbar. Could you realistically expect somebody to know how to disable cache with that? Do you even remember how (perhaps you didn’t know it’s possible). Turns out the gear in the lower right (I don’t know why it’s separated from the other buttons) is effectively a menu (despite no visual indication of this).

  2. Distinguish navigation from content. 

    It should always be apparent to the user when something is System-wide navigation, App-wide-navigation, or custom-content. This is usually done quite well and isn’t often a problem. But look at the picture below:

    Don’t you agree that uninstall button is weirdly placed? Look at it for a while, and you may realize that it’s simply a bookmark to a page entitled “Uninstall Chrome,” but there’s no systematic way to know this.

    Chrome slightly blurring the line between content and menus

  3. All errors should give you unambiguous directions to their solution.Directing to a intuitive help interface is sufficient, the point is that somebody who’s never used your system should at the very least be able to systematically find their way to the information on how to use your system.
    Dos prompt failing to direct toward help on an error

    Case Study: Dos Prompt. I remember as a lad of 9 being at the magical black terminal that responded to secret passwords (“called commands”) consisting of unguessable combinations of characters. It was reminiscent of a haunted mansion in scooby doo where the only way to get to a room would be by playing the secret piano keys. I would type almost anything except the commands I knew (qbasic, dir, cd) and would get back an ominous “Unknown command or filename.” Until one day, in an act of annoyance I typed help (I meant it as more of an imperative at the machine than anything). All of the bad command or filename messages should have mentioned this command! There was an amazing help tutorial back then in dos (not like now) that taught piping and everything with enough examples for me to learn at that age with limited vocabulary.

    I want to take a second here and hammer this point. Qbasic was one of those “magic passwords” that I learned only through my brother’s friend, my whole foray into coding was almost stymied by terrible UI.

  4. Enumerate all available action. 

    This may be the biggest failing of modern UI. Going back to the chrome example above, would you believe I didn’t know about that lower right gear for months of using chrome’s debug menu (and I certainly wasn’t the only one)? With almost any application, I routinely discover that there are hidden features available that I wish I had known existed sooner. Did you know you can drag a window to the left edge of your screen in Windows 7? Did you know you can drag tabs not only in one browser window but between browser windows/instances in chrome? Did you know that you move a window with the keyboard by pressing alt-space  (then down, then enter, then the arrow keys?)?  Did you know you can see memory usage of tabs in chrome by pressing shift-escape? And don’t get me started on gmail, office, or mac. This point is so in-depth and I have so much to say on it that it will take an entire post of its own to cover. Stay tuned for more.

 

Making it work

http://edweissman.com/dear-boss-for-a-programmer-10-minutes-3-hours

Ed Weissman posted a retelling of a scenario where his boss asked him for “10 minutes” to solve a problems. He reluctantly agrees but ultimately spends 3 hours working on this fix, and for most of the time he’s browsing Hacker News while his boss and a coworker named Sue repeatedly fumble with software while trying to convey the bug to him.

Of course everybody has opinions on the internet, but what struck me was when one commentor by the id Michael suggests that our protagonist Ed could have tried to be more helpful there was roughly a 10-1 disproportionate rebuke of Michael’s thought, mostly along the lines of: It’s not the narrator’s fault or job description to help others who are incompetent with software.

I want to look at the larger theme here. Off the top of my head, I can come up with several ways to handle that situation better (e.g. mulitask with something work-related, help out your co-workers, have bug reports get CC’d directly to you, come up with a better meeting strategy) but the root problem here is either attitude or a naive idealism that business should function mechanically..

The attitude portrayed is: I want to do my piece, and only my piece, and if you aren’t able to interface with me in a rigid predefined way, then I’ll silently resent you / the system. To look at it as a naive contractual system (programmer only does programming, manager only does meta-optimization, etc) is to miss the chance to improve things.

How would you feel if you got bounced around accounting over an issue with your check because several people said “It’s not my job” ?  This is the same situation with the roles reversed.

And moreover, how can you expect to go anywhere if you take no ownership of outcomes and can’t be liked by coworkers?

Give it a ReST.

One term that comes up significantly too much in interviews is the highly applauded and highly misunderstood [1] [2] [3] ReST.

I’m not sure why it comes up in interviews, but here are a few guesses:

  1.  As an indirect measure of how one stays up-to-date with the developer community.
  2. The interviewer is actually caught up in the trend himself, and that this “paradigm shift” is a crucial practice that can’t be learned in a matter of hours.
  3. They don’t know what else to ask, or they were asked it when they interviewed.

I think this particular question, and this type of question (even though I now know ReST so well I nail it every time) are a bad idea.

  1. By sheer virtue of the fact that it comes up so much, it can simply indicate the candidate has done so many interviews that they just happened to look it up the particular idea after stumbling when hit by it before.
  2. It’s not a direct measure of a candidate’s skill. Just as using browser version to infer a browser’s capabilities is inferior to simply directly testing the browser’s capabilities, such indirect measures of people are more error-prone.
  3. Staying absolutely up to the minute with the developer community isn’t important. In fact, by lagging behind a little (not too much) you miss out on a lot of flash-trends. Even if these trends do stick around (RoR, node.js) early adoption comes at a steep price.
  4. Knowing acronyms isn’t necessarily the best measure of being up-to-date. I think a question I hear too little, which is more important, is What’s your process for debugging an application?

So I recommend ditching the ReST question, and really any question on specific acronyms or designs that could be learned in a matter of hours.

Returning random ordered results in MS access

You may be shocked to hear, but back when I was young I used the now-hated Visual Basic and Access. In fact, moving these applications to PHP on the job is how I first learned PHP.

Anyways, once I was trying to return random results in an sql query to an Access MDB from VB / vba and I scoured the internet and found nothing. Then I devised this little number, which probably for all practical purposes does the trick:

ORDER BY right(right(now*10000000000, 4) ^ (1/ID),4)

where ID is the unique id for the given table. Right(now*100000000000,4) basically gets you a pseudo-random 4-digit value, the current milliseconds. We combine this with the row ID to give a different but constantly changing number for each row.