Skip to content

Instantly share code, notes, and snippets.

@joshuajhun
Last active October 31, 2016 15:47
Show Gist options
  • Save joshuajhun/14db294c5af455b249b669a1ed533fdb to your computer and use it in GitHub Desktop.
Save joshuajhun/14db294c5af455b249b669a1ed533fdb to your computer and use it in GitHub Desktop.
Higher Level Functions

Partial Functions and Currying

clone these yung-repos

partialFunctionPlayground

tetris

#Context

Why this matters.

Programming is our craft. It is what we do and what we will do as developers and we should hold ourselves to standard. There is a lot of bad code out there (I wrote most of that code). So if we're talking about striving to write good code there are a couple of things for us to consider.

  • SRP - Single responsibility principle
  • DRY - Do not repeat yourself
  • Agnostic - Your code doesn't care about what's being passed through. It will execute.

Historically. If we hold ourselves to this standard we are going to write good, scalable code. Thats the hard part with all of this. If our code (Object Orient Code) is dependent upon something that is going to change it needs to be able to accommodate that change. If we are unable to accommodate that change our code isn't good.

We know the no matter how robust your code is it will break at some point down the line. Ways we can combat this is to have your code be more granular. If it's more granular we know exactly where our code breaks.

Partial Functions

JavaScript's flexibility allows us to partially use a function which will allow us to essentially dry up our code. The less code we write the less bugs we'll have. This also gives us an opportunity for code to be reused.

let's look at an easy example, You'll implement it, and then we'll look at some code together and refactor it using partial functions!


function add(a, b) {
  return a + b;
}

var addTwo = add.bind(null, 2);

addTwo(4); // 6

So this is a perfect example for how bind can really help us dry up our code. In the code featured here we are seeing we've referenced the add function and we're using the bind function to pass in the two arguments to it.

We are essentially allowing ourselves to reuse a part of a function. If our code was repetitive this would allow multiple functions to essentially use a part or a piece of a function again.

Your Turn

Let's take baby steps towards actually making this stuff click!

A function that adds 1 to a number.

A function that subtracts 3 from a number.

A function that doubles a number.

A function that halves a number.

If you're lost here are the answers!

####A function that adds 1 to a number

function add(a, b){
  return a + b; 
}

var addOne = add.bind(null, 1)

####A function that subtracts 3 from a number.

function takeAway(a, b){
  return a - b;
}

var minusThree = takeAway.bind(null,3)

minusThree(6)

####A function that doubles a number.

function doubleNumber(a, b){
  return a * b
}

var doubleMe = doubleNumber.bind(null, 2)

doubleMe(2)

A function that halves a number.

function divide(a, b){
return b / a 
}

var halveMe = divide.bind(null,2)

halveMe(6)

Practical Applications

You are going to find repetitive code everywhere. When you see repetitive code it should make us uncomfortable. It should make us want to refactor!

Lets look at an example of code that I've wrote and refactored using partial functions.

var util = require('util');
var EventEmitter = require('events');
util.inherits(Block, EventEmitter);

function Block(board, x = 0, y = 0) {
  this.board = board;
  this.x = x;
  this.y = y;
  this.active = true;
};

Block.prototype.inactive = function() {
  return this.active = false;
};

// check the position of the block on the board

Block.prototype.blockIsAtBottomOfBoard = function() {
  return this.y + 1 > this.board.rows;
};

Block.prototype.blockIsAtLeftSideOfBoard = function() {
  return this.x - 1 < this.board.columns;
};

Block.prototype.blockIsAtRightSideOfBoard = function() {
  return this.x + 1 > this.board.columns;
};

//

// can check whether their is a block to the right/left/below

Block.prototype.isThereABlockOnTheRight = function() {
  if (this.board.findBlockOnBoard(this.x + 1, this.y)) { return true }
};

Block.prototype.isThereABlockOnTheLeft = function() {
  if (this.board.findBlockOnBoard(this.x - 1, this.y)) { return true }
};

Block.prototype.isThereABlockBelow = function() {
  if (this.board.findBlockOnBoard(this.x, this.y + 1)) { return true }
};

// 

// check whether the block can move left, right, or down on board

Block.prototype.canMoveDown = function() {
  if (this.inactive){ return this.active = false }
  
  // check conditionals below to confirm falling block moves correctly
 
  if (this.blockIsAtBottomOfBoard){ return this.inactive }
 
  if (this.isThereABlockBelow) { return false }
};

Block.prototype.canMoveLeft = function(){
 
  if (this.inactive) { return this.active = false}
 
  // check conditionals below to confirm falling block moves correctly
 
  if (this.blockIsAtLeftSideOfBoard) {return this.inactive}
 
  if (this.isThereABlockOnTheLeft) { return false }
};

Block.prototype.canMoveRight = function(){
  
  if (this.inactive) { return this.active = false }
  
  // check conditionals below to confirm falling block moves correctly
  
  if (this.blockIsAtRightSideOfBoard) { return this.inactive }
  
  if (this.isThereABlockOnTheRight) { return false }
};

// move block on board if it meets conditionals

Block.prototype.moveDown = function() {
  if (this.canMoveDown) { this.y++ };
  return this;
};

Block.prototype.moveRight = function() {
  if (this.canMoveRight) { this.x++ };
  return this;
};

Block.prototype.moveLeft = function() {
  if (this.canMoveLeft) { this.x-- };
  return this;
};

module.exports = Block;

so as you can see there is a ton of code here that is repeated. One thing I tried to do was write code that was granular. So we do have multiple functions working together which was great for debugging. Because a lot of the code in this program are doing very similar things we can essentially refactor a part of that.

Let's start with something easy and work on refactoring pieces and parts of this code together.

Block.prototype.moveDown = function() { if (this.canMoveDown) { this.y++ }; 
return this;};
 
Block.prototype.moveRight = function() { if (this.canMoveRight) { this.x++ }; return this;}; 
Block.prototype.moveLeft = function() { if (this.canMoveLeft) { this.x --}; return this;};

so if we look at this code the code is pretty repetitive. Really the only thing that is changing here is the x and the y. Which is our offsets. What would be really cool is if these 3 functions pointed to one function. What would also be great is if we refactor our code our tests don't break.

Right now my test are expecting methods called moveRight(), moveLeft(), and moveDown().

So lets refactor!

Step 1 pull the MVP!

We need to find a way to isolate the parts and pieces we need for this function to work. So if we're looking at our code the pieces we need are going to be the x and the y. Let's make a function called move and lets just make sure this function receives the x and the y offset.


Block.prototype.move = function(xOffset, yOffset){
 // stuffz will go in here 🐼
 // grab the current object's x and add the xOffset to it
 // grab the current object's y and add the yOffset to it
 // return the current object 
 }

So now that we have our sudo code written lets look into each piece of code that needs to be implemented.

Step 2 Finishing the logic.

first of all if we are going to write code the way we want it, it would be nice to reference our object by calling this. We could pass the object through but if we are going to use bind then it will be a little unnecessary to pass the object through when we can utilize this

our code should look a little like this.

Block.prototype.move = function(xOffset, yOffset){
	this.x += xOffset
	this.y += yOffset 
	return this
}

Step 3 have the previous function point to our new one.

If we look at our previous function it looks like this.

Block.prototype.moveDown = function() { if (this.canMoveDown) { this.y++ }; 
return this;};

Block.prototype.moveRight = function() { if (this.canMoveRight) { this.x++ }; return this;}; 
Block.prototype.moveLeft = function() { if (this.canMoveLeft) { this.x --}; return this;};

so lets look at one of the functions

if we were to take the Block.prototype.moveDown function and have it point to move we could do something a little like this

 if (this.canMoveDown){this.moveDown = this.move.bind(this, 0, +1);}

so now if we finish this out our functions should look like this.

function Block(board, x = 5, y = 5, color = “#000000”) {

this.board = board;
this.active = true;
this.x = x;
this.y = y;
this.height = 1;
this.width = 1;
this.color = color;

if(this.canMoveDown){this.moveDown = this.move.bind(this, 0, +1);}
if(this.canMoveLeft){this.moveLeft = this.move.bind(this, -1, 0);}
if(this.canMoveRight){this.moveRight = this.move.bind(this, +1, 0);
}
}
Block.prototype.move = function (xOffset, yOffset) {
this.x += xOffset;
this.y += yOffset;
return this

#Lets do one more together. lets look at the following code.

Block.prototype.blockIsAtBottomOfBoard = function() {
 return this.y + 1 > this.board.rows;
};

Block.prototype.blockIsAtLeftSideOfBoard = function() {
 return this.x - 1 < 0;
};

Block.prototype.blockIsAtRightSideOfBoard = function() {
 return this.x + 1 > this.board.columns;
};

We are obviously repeating ourselves here. really the only things changing about these functions are the comparisons and the boards and columns that we are comparing. so lets write a function called isAt that can take in these parameters for us.

Block.prototype.isAt = function(offset, comparison, board){
	//do the things. 
}

Now we have to pass a comparative statement through and the way we do that is define that stuff as variables.

Block.prototype.isAt = function(offset, comparison) {
   // do the things
};

var compareLess = function(a,b) {
  return a < b;
};

var compareGreater = function(a,b) {
  return a > b;
};

Now we need to somehow bind these functions together.

so let's start with the first portion of the code.

this.blockIsAtBottomeOfBoard = this.isAt.bind(this.y, +1, compareGreater, this.board.rows)

Which means we have to then change our isAt function to accommodate for the things we are binding to this function.

Whats nice about javascript is that there is a way for us to explicitly return a function. It's going to look a little like this.

Block.prototype.isAt = function(offset, comparison, board){
	return comparison((this + offset), (board))
}

  this.blockIsAtBottomOfBoard    = this.isAt.bind(this.y, +1, compareGreater,this.board.rows);
  this.blockIsAtLeftSideOfBoard  = this.isAt.bind(this.x, -1, compareLess, 0);
  this.blockIsAtRightSideOfBoard = this.isAt.bind(this.x, +1, compareGreater,this.board.columns);```
Block.prototype.isAt = function(offset, comparison,board) {
  return comparison((this + offset),(board));
};

var compareLess = function(a,b) {
  return a < b;
};

var compareGreater = function(a,b) {
  return a > b;
};

###your turn

There is a lot of functions that need to be refactored in all of this code. I would like you to apply the ideas we have started here. If you get lost feel free to checkout my refactored-branch to walk you through some of the refactoring techniques that I've done. The code that needs to be refactored are as follows!

Chunk 1 (medium)

Block.prototype.canMoveDown = function() {
  if (this.inactive){ return this.active = false }

  // check conditionals below to confirm falling block moves correctly

  if (this.blockIsAtBottomOfBoard){ return this.inactive }

  if (this.isThereABlockBelow) { return false }
};

Block.prototype.canMoveLeft = function(){

  if (this.inactive) { return this.active = false}

  // check conditionals below to confirm falling block moves correctly

  if (this.blockIsAtLeftSideOfBoard) {return this.inactive}

  if (this.isThereABlockOnTheLeft) { return false }
};

Block.prototype.canMoveRight = function(){

  if (this.inactive) { return this.active = false }

  // check conditionals below to confirm falling block moves correctly

  if (this.blockIsAtRightSideOfBoard) { return this.inactive }

  if (this.isThereABlockOnTheRight) { return false }
};

hint:

  • maybe look into breaking the function into one piece or part (maybe calling it canMove)
  • look into passing in the parts of the function that change (bind the function, first argmument is this, second and third arguments are the changing parts.)

Chunk 2 (easier)

Block.prototype.isThereABlockOnTheRight = function() {
  if (this.board.findBlockOnBoard(this.x + 1, this.y)) { return true }
};

Block.prototype.isThereABlockOnTheLeft = function() {
  if (this.board.findBlockOnBoard(this.x - 1, this.y)) { return true }
};

Block.prototype.isThereABlockBelow = function() {
  if (this.board.findBlockOnBoard(this.x, this.y + 1)) { return true }
};

hint:

  • the only parts that are changing are the offsets
  • look into making one function that will take in the specific offsets
  • it looks like it's always returning true.

Recap

  1. Find repetitive code
    • if you find yourself repeating yourself it might be time you refactor your code into partial functions
  2. Have your repeated code point to a reusable function
    • Take the pieces you need to make the function work, and delegate those important pieces to another function
  3. Make it agnostic as possible.
    • So what your function is taking a bunch of arguments. It's reusable.

Recursion

Recursion feels a lot like exorcise. I know I should do it, but I also have asthma and I might die. I don't like dying.

But if we can effectively leverage recursion not only will we be better off but we'll be pushing to have that resourceful mindset on the forefront of our minds.

I've found that finding the parts and pieces that are repetitive and making sure we utilize those in our function.

So one issue we can run into in recursion is overflowing our stack. If we do that it will essentially blow up in our face. For example

function chicken() {
  return egg();
}
function egg() {
  return chicken();
}
console.log(chicken() + " came first.");\

Uncaught RangeError: Maximum call stack size exceeded

if we were to take this function and call it we're going to get an error. there is no escape route for our function and essentially what happens is it infinitely loops.

So why don't we actually write a function that utilizes recursions and then we can look at a more practical use for our code.

Your Turn!

Write a function called countdown(), which takes a number and counts down from the number passed in to 0 by recursively calling itself. If you called countdown(4), it should console.log(4), console.log(3), console.log(2), console.log(1), and—finally— console.log(0).

Answer:

function countDown(number){
  if(number > 0){
    console.log(number)
  return countDown(number - 1)
	} else {
	return number
  }
}

#Currying

The last thing we're going to talk about is currying. This is like the partial functions cool, kinda awkward cousin. So we've talked about how flexible javascript is. Whats nice about that is we can essentially stagger our function calling. The type of programming we are going to be doing is basically functional.

but why?

So if you're looking at your code and it is super repetitive there are ways we can clean that up. Currying is a way of constructing functions that allows partial application of a function’s arguments. What this means is that you can pass all of the arguments a function is expecting and get the result, or pass a subset of those arguments and get a function back that’s waiting for the rest of the arguments. It really is that simple.

lets do our first curry here.

var greet = function(greeting, name) {
  console.log(greeting + ", " + name);
};

so obviously we've created a function that totally works. If we wanted it to be more versatile we could totally curry this and make it more flexible and usable.

function curriedGreeting(greeting){
  return function(name){
    console.log(greeting + ", " + name);
  } 
}
	

So at this point in time you might be wondering what just happened. Well whats nice about this is we've created a partial function that can be used in multiple ways.

consider this example

var yungGreeting = curriedGreeting('hello' )

yungGreeting('yung-jhun') // "hello, yung-jhun"

alternatively you could also pass the function multiple arguments

curriedGreeting('hello', 'yung-jhun')

The benefits of something like this is that you only have to look to one place to really get your function to work.

###Your turn.

Consider this code and find out a way to refactor it into a partial function.

function wrapInParagraphTags(body) {
  return '<p>' + body + '</p>';
}

function wrapInHeaderTags(body) {
  return '<h1>' + body + '</h1>';
}

function wrapInListItemTags(body) {
  return '<li>' + body + '</li>';
}

Answer:

function createCurriedWrapper(tagName) {
  return function (body) {
    return '<' + tagName + '>' + body + '</' + tagName + '>';
  };
}

var wrapInParagraphTags = createCurriedWrapper('p');
var wrapInHeaderTags = createCurriedWrapper('h1');
var wrapInListItemTags = createCurriedWrapper('li');

var h2 = createCurriedWrapper('h2')('Hello world');

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment