2. Write performant, stable, readable,
debuggable, and production worthy code
quickly.
In short, to make life easier.
I won't invoke complex vocabulary, math, and
theory; I don't need to.
What is my goal here?
3. What is functional programming?
● Avoids State
● Emphasizes application of functions
● Avoids side-effects
● Combines functions
● Solves problems conceptually (no loops)
4. What is fancy?
Fancy is a mashup:
a. Underscore.js functions (Jeremy Ashkenas)
b. Functional.js features (Oliver Steele)
c. 1-off pseudo-arrays much like a jQuery collection
d. And some other functions I felt were needed
(chunk)
FancyArray([1,2,3]).sortBy("%2");
>>> [2, 1, 3]
a b
c
5. Wait, how do I use this?
1. Construct a FancyArray([foo, bar]) from any array.
Construct a FancyObject({foo: bar}) from an object.
2. Call normal underscore functions on your FancyArray (or object).
3. When one of those calls would return an array, it instead returns a
FancyArray, thus allowing chaining. When one of underscore's functions
would have returned an object, it instead returns a FancyObject.
However, unlike underscore's .chain method, these one-off objects act very
much like normal arrays or objects.
6. And-
Also, any function that takes another function as an
argument (like map) can take a string to represent that
function as devised by Oliver Steele
FancyArray([1,2,3]).map("*2")
>> [2,4,6]
FancyArray([1,2,3]).map("12/x")
>> [12,6,4]
FancyArray([1,2,3]).map("x,y->x+y")
>> [1,3,5]
7. Like this:
var samplePeople = new FancyObject(
{
0: {name: "Alice", age: 15, ranking: 3, team: 2},
1: {name: "David", age: 25, ranking: 2, team: 1},
2: {name: "Charlie", age: 32, ranking: 4, team: 1},
3: {name: "Bob", age: 24, ranking: 1, team: 4}
});
samplePeople.values().sortBy(".age").pluck("name");
>>>["Alice", "Bob", "David", "Charlie"]
Returns a FancyArray
Though you may not see
from the chrome console
output, this is still a
FancyArray
Functional.js style function
8. How does that make my life easier?
You can read your code left-to-right, like english
It's now shorter than declarative, with less ugly nesting.
No pollution of public scope or prototypes.
var samplePeople = { ... };
// fancy - This is prettier
FO(samplePeople).values().sortBy( ".age").pluck("name");
// plain underscore - Than that
_.chain(samplePeople).values().sortBy(
function(p) { return p.age; }).pluck( "name").value();
9. Let's consider a sample problem
● Not fibonacci
● Not prime numbers
● Not foobar or fizzbuzz.
Evaluating poker hands.
10. How do poker hands work?
1st - Hand Types:
1. Straight Flush
2. 4 of a kind
3. Full-House
4. Flush
5. Straight
6. 3 of a kind
7. 2 Pair
8. 1 Pair
9. High card
2nd - Card Values
How high are the cards
used to make up the
hand?
3rd - Card Values
How high are the
remaining cards in the
hand?
11. Example:
Full House:
3 Of 1 value
2 Of another Value
Beats lower ranking hands
(like a straight). Loses
to higher-ranking hands.
(Straight flush).
If two full houses happen, compare the sets of 3, whichever has the higher
value wins. If those are equal, compare the pairs of two, whichever has the
higher value wins. If those are equal, then the hands are equal.
12. A lame approach
// A non-functional approach might involve defining a lot of functions that
// look something like this
/**
* Returns true if hand is full-house
*/
function isFullHouse(cards) {
var differentCards = [] ;
for (var x=0; x < cards.length; x++) {
differentCards[ valueOf( cards[x] ) ]++;
}
return (differentCards. indexOf(3) > 0) && (differentCards. indexOf(2) >
0);
}
function compareTwoFullHouses (fullhouse1, fullhouse2) {
...
13. Let's think about this...
What do poker hands have in common?
● 6 hands: Sets of Cards that are the same value.
● 2 hands: Cards that have consecutive value
● 2 hands: Cards that are all the same suit.
Or
● 8 hands: Counts of cards that share sameness on an attribute
● 2 hands: Cards that have consecutive value
14. Coming up with an algorithm
1. Take the hand. Extract relevant
attribute. In this case, it means turn
[ "3D", "3S", "6D", "2C", "2H"]
into :
[ 3, 3, 6, 2, 2]
2. Find groups of cards. Turn it
into
{3: 2, 6: 1, 2: 2} (meaning two threes, a six, and a pair of twos)
3. Compare this to the hand definition.
4. (For ties) Find a way to convey the second and third characteristics (in this
case, that 6 of diamonds).
15. A functional approach
[
["Full House", followsPattern(cards, [3,2], cardValues)],
["2 Pair", followsPattern(cards, [2,2,1], cardValues)],
["Flush", followsPattern(cards, [5], cardSuits)],
]
var followsPattern = function(hand, expected, callback) {
var values = FA(hand).map(callback); //Turn the input to an array of numbers
var groups = values.countBy(); //Get the counts of duplicates
if (! groups.values().hasAll(expected)) {
return false; //This hand is NOT of the expected type, no match
}
var sets = groups.pairs(); //Turn {2: 3, 8: 2} into [[2, 3], [8, 2]] for example
sets = sets.sortBy('x[1] + "" + x[0]');
//take advantage of alphabetical sort, sort by cardCount,
// then card Value alpha in a tie
return sets.pluck(0).join("");//pluck the first item, the cardValueAlpha
};
16. Example Helper Function
/**
* return true if every value in needle is in this array,
* at least as many times.
*/
FancyArray.prototype.hasAll = function(needle) {
var summed = this.countBy().meld(
FA(needle).countBy().mapObj( "a*-1"),
"a+b");
return summed.values().every( ">=0");
};
17. So... Is it really better?
Advantages:
● Less Code
● More Dry
● Easier to keep track of
● Easier to extend (say
make it work with
hold'em)
● A little faster to write
Disadvantages:
● Perhaps scary at first
● A little harder to
wrap your brain
around
● Could be off-putting
to collaborators who
know nothing about
the notation.
19. Is this really functional programming?
Isn't there more than this to functional programming?
A challenge has been leveraged at underscore, namely by Brian
Lonsdorf. In his talk, "Underscore, You're doing it wrong" in which he
challenged whether underscore really enabled functional
programming. In short he argued that underscore's notation impeded
currying and composition.
It's a fair criticism, and one that applies equally well to the
ecmascript 5 standard which put map in the array.prototype. Really,
you can't map to another map eloquently or do other advanced
functional programming things with the same grace. However, this
isn't a big issue for the vast majority of users.
20. How does it stack up?
● Avoids State
● Emphasizes use of functions
● Avoids side-effects
● Combines functions
● Solves problems conceptually
Call it whatever you want. The important thing is that you
get the ability to write good, reusable, abstracted code
easily. Don't optimize for an edge-case.