24
Ramda let's write declarative js Roman Rodych Innocode

Ramda lets write declarative js

Embed Size (px)

Citation preview

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!