ramda lets write declarative js

24
Ramda let's write declarative js Roman Rodych Innocode

Upload: pivorak-meetup

Post on 13-Apr-2017

219 views

Category:

Software


0 download

TRANSCRIPT

Ramdalet's write declarative js

Roman RodychInnocode

What this talk about?

Approaches

Motivation (why?)

What we want?

● Describe what to do instead of how● Avoid duplication● Stop reinventing common functionality● Composition● Less code - less bugs))

Solutions (how?)

Accept functions as arguments[1, 2, 3].map(function (x) {

return -x

})

Return function as resultfunction fn (a, b) {

return a + b

}.bind(undefined, 1, 2) // => fn(1, 2)

Higher order functions

Curryingvar add = R.curry(function (x, y) {

return x + y

})

add(4, 2) // 6

add(4)(2) // 6

var add4 = add(4) // fn !

add4(2) // 6

add() // fn !

Function first, data lastvar data = [ {id: 1, price: “33.00”, amount: 798},

{id: 5, price: “22.00”, amount: 0}, … ]

// Our task is to get all sold products

var getProducts = R.filter(

R.propEq(“amount”, 0)

) // (list) => list.filter( (item) => item.amount === 0 )

getProducts(data) // [{id: 5, price: “22.00”, amount: 0}, ...]

Dictionary for the next slideR.pipe - Performs left-to-right function composition. The leftmost function may have any arity; the remaining functions must be unary.R.prop - Returns a function that when supplied an object returns the indicated property of that object, if it exists.R.__ - A special placeholder value used to specify "gaps" within curried functions, allowing partial application of any combination of arguments, regardless of their positions.R.gt - (a, b) => a > b

Functions composition e.g.: pipe, compose...var data = [ {id: 1, price: “150.00”, amount: 798},

{id: 5, price: “22.00”, amount: 0}, … ]

// Now task is to get “id” and “price” of all available and

expansive products

var available = R.pipe(R.prop(“amount”), R.gt(R.__, 0))

// (item) => item.amount > 0

var expansive = R.pipe(R.prop(“price”), parseFloat, R.gt(R.__, 100))

// (item) => parseFloat(item.price) > 100

...

Functions composition IIvar data = [ {id: 1, price: “150.00”, amount: 798},

{id: 5, price: “22.00”, amount: 0}, … ]

...

var filterProducts = R.filter(

R.allPass([available, expansive]))

var getProducts = R.pipe( filterProducts, R.pick([“id”, “price”]) )

getProducts(data) // [{id: 1, price: “150.00”, amount: 798}, ...]

Point free - function evolutionPast (dark ages)function makeMeHappy() {

b = a + 42 // a and b are somewhere in the scope

}

Present (renaissance)const makeMeHappy = a => a + 42

Future (post-postmodernism but nobody knows, really...)const makeMeHappy = R.add(42)

Dictionary for next slideR.converge - Boring long definition…R.find - Returns the first element of the list which matches the predicateR.nthArg - Returns a function which returns its nth argument

Point free II “overrefactoring”var numberPropGt = R.curryN(3, R.

converge(

R.gt,

[ R.pipe(

R.converge(R.prop,

[R.nthArg(0),

R.nthArg(2) ]),

parseFloat ),

R.nthArg(1)

]

))

var available = numberPropGt(“amount”, 0)

// (item) => parseFloat(item.amount) > 0

var expansive = numberPropGt(“price”, 100)

// (item) => parseFloat(item.price) > 100

...

Point free III real example// findById :: String -> Array -> Object

const findById = R.converge(

R.find,

[

R.pipe(R.nthArg(0), R.propEq("id")),

R.nthArg(1)

]

)

Price $$$

● easy● fast● lightNot so

Should you? (when?)

When to use

● DSLs● Rich business logic handlers● Your option...

Alternatives (what else?)

Thanks! Maybe(“applauds”)

Beer time!