Loading

Functional Programming In JavaScript

Steve Smith

This is a live streamed presentation. You will automatically follow the presenter and see the slide they're currently on.

Functional Programing

In JavaScript

λ

What is functional programming?

An alternative to procedural or object-oriented programming

functions are the basic building block

functional programming

  • minimizes side-effects
  • avoids mutation
  • encourages explicit inputs and outputs

why?

  • easy to think about in isolation
  • easier to test
  • higher level of abstraction
  • easier to refactor

Some basic patterns

map and filter

var R = require('ramda')
//ramdajs.com

R.map(
    (x) => 2 * x,
    [1, 2, 3, 4, 5])
//returns [ 2, 4, 6, 8, 10 ]

R.filter(
    (x) => x % 2 === 0,
    [1, 2, 3, 4, 5])
//returns [ 2, 4 ]

reduce

R.reduce(
    (sum, x) => sum + x,
    0,
    [1, 2, 3, 4, 5])
// returns 15

// (0, 1)  =>  1

// (1, 2)  =>  3

// (3, 3)  =>  6

// (6, 4)  => 10

// (10, 5) => 15

curry

not a sauce! :P

var add_3 =
    R.curry((a, b, c) => a + b + c)

//All of these return 6
add_3(1, 2, 3)
add_3(1, 2)(3)
add_3(1)(2, 3)
add_3(1)(2)(3)
//All ramda functions are auto-curried

var doubles = R.map((x) => 2 * x)
var evens = R.filter((x) => x % 2 === 0)
var sum = R.reduce((x, y) => x + y, 0)

doubles([1, 2, 3, 4, 5])
//returns [ 2, 4, 6, 8, 10 ]

evens([1, 2, 3, 4, 5])
//returns [ 2, 4 ]

sum([1, 2, 3, 4, 5])
//returns 15

compose

var f = (x) => 2 * x
var g = (x) => x + 1
var h = (x) => x / 2

var fogoh =
    R.compose(f, g, h)

/* fogoh = (x) =>
    2 * ( (x / 2) + 1) */

fogoh(2) //returns 4

compose + curry

var is_number = (x) => !isNaN(x)
var sum = R.reduce((x, y) => x + y, 0)

var sum_what_you_can =
    R.compose(
        sum,
        R.filter(is_number),
        R.map(parseInt))

sum_what_you_can([
    12,
    "42",
    "potatoes",
    87
])
//returns 141

Objects

var purchase = {
    name: 'hover board',
    price: 10000
}

var get_price = R.prop('price')

get_price(purchase)
//returns 10000
var change_price = R.assoc('price')

change_price(100, purchase)
/*
returns {
    name: 'hover board',
    price: 100
}
*/
var price_lens =
    R.lens(
        R.prop('price'),
        R.assoc('price'))

var apply_discount =
    R.curry((discount, purchase) =>  
        R.over(
            price_lens,
            R.subtract(R.__, discount),
            purchase))

apply_discount(100, purchase)  
/* returns {
    name: 'Hover Board',
    price: 9900
} */
var price_formatter_lens =
    R.lens(
        R.prop('price'),
        R.assoc('formatted_price'))

var add_formatted_price =
    R.over(
        price_formatter_lens,
        format_dollars)

add_formatted_price(purchase)
/*
returns {
    name: 'hover board',
    price: 10000,
    formatted_price: '$10,000'
}
*/

No side Effects

//modifies value and returns it
var apply_discount = function (discount, purchase) {
    purchase.price -= discount
    return purchase
}

apply_discount(100, purchase)
/*changes value of purchase to {
    name: 'Hover Board',
    price: 9900
} */

apply_discount =
    R.curry((discount, purchase) =>
        R.over(
            R.lensProp('price'),
            R.subtract(R.__, discount),
            purchase))

apply_discount(100, purchase)
/* returns {
    name: 'Hover Board',
    price: 9900
} */
var add = (a, b) => {
    var sum = a + b
    console.log(sum)
    return sum
}

explicit inputs

//implicit
var purchase = {
    name: 'hover board',
    price: 10000,
    apply_discount: function (discount) {
        this.price -= discount
    }
}

//explicit
apply_discount =
    R.curry((discount, purchase) =>
        R.over(
            R.lensProp('price'),
            R.subtract(R.__, discount),
            purchase))

explicit outputs

var change_item_name =
    function (purchase, name) {
        purchase.name = name
    }

change_item_name(purchase, 'light saber')
/* changes purchase to
{ name: 'light saber', price: 10000 }
*/

change_item_name = R.assoc('name')

change_item_name('flying car')
/* returns
{ name: 'flying car', price: 10000 }
*/

Easy to think about in isolation

var add = (a, b) => a + b

Easier to test

describe('add', () => {
    var add = require('../add.js')

    it(
        'should return the sum of its two arguments',
        function () {
            expect(add(1, 1)).toEqual(2)
        })
})

easy to refactor

var format_dollars_cents =  
    R.compose(
        R.join('.'),
        (money) => [
            format_dollars(money[0]),
            money[1] ? money[1].slice(0, 2) : '00'],
        R.split('.'),
        R.toString)

format_dollars_cents(100000)  
//returns $100,000.00

format_dollars_cents(100000.234)  
//returns $100,000.23

format_dollars_cents(.234)  
//returns $0.23

higher level of abstraction

var comma_separate = (width) =>  
    R.compose(
        R.join(','),
        R.map(R.reverse),
        R.reverse,
        R.splitEvery(width),
        R.reverse)

var format_dollars =  
    R.compose(
        R.concat('$'),
        comma_separate(3),
        R.toString)

format_dollars(100000)  
//returns $100,000

debugging

var comma_separate = (width) =>
    R.compose(
        R.join(','),
        R.reverse,
        R.splitEvery(width),
        R.reverse)

var format_dollars =
    R.compose(
        R.concat('$'),
        comma_separate(3),
        R.toString)

format_dollars(100000)
//returns '$001,000', what gives?
var tap = function (x) {
    console.log(x)
    return x
}

var comma_separate = (width) =>
    R.compose(
        R.join(','),
        tap,
        R.reverse,
        R.splitEvery(width),
        R.reverse)

//prints [ '001', '000' ]
var comma_separate = (width) =>
    R.compose(
        R.join(','),
        R.map(R.reverse),
        R.reverse,
        R.splitEvery(width),
        R.reverse)

This is just the beginning

  • Functors
  • Maybe
  • Either
  • Monads
  • Applicative Functors
  • Category Theory

Resources

Professor Frisby's Mostly Adequate Guide to functional programming

https://drboolean.gitbooks.io/mostly-adequate-guide/content/

Ramda

ramdajs.com

lodash

lodash.com

lodash-contrib

github.com/node4good/lodash-contrib

Learn you a haskell for great good

learnyouahaskell.com

funkyjavascript.com

Made with Slides.com