Function composition in Javascript


Published on

Higher-order functions, function composition, managing side-effects etc in Javascript

Published in: Technology, Business
1 Like
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Function composition in Javascript

  1. 1. FUNCTION COMPOSITION Compostion • A powerful technique in functional programming • Allows one to create new functions by reusing existing functions • Specifically, function composition is a pointwise application of one function to another to create a third function: • Given two functions f(x) ->y and g(x) ->z composition refers to the method using which to yield a new function : , • h(x)-> f(g(x)) - a function created by applying function f to the value returned by the application of function g to x. “Functions” refers to “pure” functions- i.e functions that return the same value if they are applied to the same argument.
  2. 2. COMPOSE FUNCTION IN JAVASCRIPT function compose (f,g) { return function (x) { return f(g(x)); } } Example: function inc(x) { return x=1} function dbl(x) { return x*2} var incdbl= compose(inc, dbl ); var dblinc= compose(dbl, inc) console.log(incdbl(10)) ->inc(dbl(10))-> inc(20)-> 21 Console.log(dblinc(10)) -> dbl(inc(10))-> dbl(11)->22 <- a higher order function that <- returns a function that : <- applies function f to the value returned by applying function g to its argument x – compose( inc, dbl) creates a function that takes functions inc and dbl as arguments and returns a function : somefun(x) { return inc(dbl(x))} – Similarly for compose(dbl,inc)
  3. 3. COMPOSING MORE THAN TWO FUNCTIONS Example: function sqr (x) {x*x} function cube(x) { return x*x*x} Inc_dbl_sqr= compose(inc, compose(dbl,sqr)) console.log(inc_dbl_sqr(10))-> inc(dbl(100))-> inc(200)-> 201 Since compose is a higher order function that returns a function , the result of applying two functions to compose can be used as an argument to compose itself -> compose(inc, function (x) { return (dbl(sqr(x)}) which maps to: f(x) { return inc(dbl(sqr(x)))}
  4. 4. OBVIOUSLY…. • Since composition means applying the second function with the value returned by the application of the first function, the value returned by the first function must be of the same TYPE as the type of the argument required by the second function. • Functions must be “pure”. They cannot have any side effects.
  5. 5. ADDING SIDE EFFECTS Inc= function (x){ console.log(“exec inc()..) return x=1 } dbl= function(x) { console.log(“exec dbl()”) return x+x; } Etc Etc – Suppose we want to add a trace to all these functions. inc, dbl, sqr etc. – The dumb way to do this will be as given on the left- adding log statements into each of these functions
  6. 6. ADDING SIDE EFFECTS-2 inc= function (x){ var ret={} ret.val= x+1; ret.trace= “exec inc()” return ret; } dbl= function(x) { var ret= {}; ret.val=x+x; ret.trace=“exec dbl()” return ret; } Etc Etc – the better way would be to create a wrapper that returns the return value of the function and a string – This is still not a great way because now one has to create wrapper objects for every function. – How about using higher-order functions to simplify this process?
  7. 7. ADDING SIDE EFFECTS-3 function makeTraceble( f, tracestr){ return function(){ var ret={}; ret.val=f.apply(this, arguments); ret.trace=tracestr; return ret; } } trace_inc= function(inc,”exec inc()”) trace_dbl=function(dbl,”exec_dbl”) console.log(trace_inc(10))-> Object { val=11; trace=“exec inc()”} – makeTraceabe takes a function and a string as its arguments and • • Returns an object that has two members val and trace -> takes inc and string “exec inc()” and returns a function that returns an object with the members: { val-> inc.apply(this, arguments), trace-> “exec inc()” }
  8. 8. OOPS! WHAT HAPPENED TO “COMPOSABILITY”? trace_inc= function(inc,”exec inc()”) trace_dbl=function(dbl,”exec_dbl”) var fn= compose(trace_inc,trace_dbl) console.log(fn(10)) <- will this work? Can we compose these functions? NO trace_inc expects a number whereas trace_dbl returns an Object { val, trace} For composability to work, the first function must return that is a valid argument type for the second one.
  9. 9. OOPS! WHAT HAPPENED TO “COMPOSABILITY”? Can we compose these functions? trace_inc= function(inc,”exec inc()”) trace_dbl=function(dbl,”exec_dbl”) var fn= compose(trace_inc,trace_dbl) NO console.log(fn(10)) <- will this work? trace_inc expects a number whereas trace_dbl returns an Object { val, trace} For composability to work, the first function must return that is a valid argument type for the second one.
  10. 10. BRINGING BACK THE ABILITY TO COMPOSE The dumb way to bring back “composability “would be to: – Rewrite inc(), dbl() , sqr() etc in such a way that they accept an a object of type { val, trace} and return an object of the same type. – But then we are back to rewriting all the functions
  11. 11. BRINGING BACK THE ABILITY TO COMPOSE- A BETTER WAY function unit(x) { var M={} M.val=x; M.trace=“” return M; } function bind( M, fn) { var retM={}; retM=fn(M.val) retM.trace=M.trace+retM.trace } bind(unit(1000), trace_inc) ->{ val=1001; trace=“exec inc()”} bind(unit(1000), trace_dbl) -> {val=2000, trace=“exec_dbl())} bind(bind(unit(1000), trace_inc), trace_d bl) -> bind( {1001,”exec inc()}”, trace_dbl) -> {val=2002, trace=“exec inc() exec dbl()”} <- unit () functon takes any number and creates an object of type {val, trace} <- bind() takes an object M of type {val, trace} returns another object of the same type <- the function Fn takes a number as its argument and returns an an object of type {val,trace} <-this is equivalent to: compose(trace_inc, trace_d bl)
  12. 12. ANOTHER EXAMPLE function make_maybeFn(f) { return function(v){ if(v!=null && typeof(v)== „number‟) { return f(v); } return "Error"; } } maybe_inc=make_maybeFn(inc)) This is a common pattern. Functions that need to be modified to validate their arguments. When such functions are composed, the second function is likely blow-up if the first one returns an error. <- make_maybefn is a higherorder function that returns a function that: - Takes a value v as its argument Applies the function f to v only if it is a valid argument Other wise it returns an error <- maybe_inc() is a equivalent to: function(v){ if(v!=null && typeof(v)==„number‟) return inc(v); } return "Error"; } {
  13. 13. ANOTHER EXAMPLE- MAYBE mb_unit= function(x){ var m={}; m.val=y; return m } mb_bind=function(M, fn) { var retM={} retM=fn(M.val); return retM; } mb_bind (mb_unit(10), maybe_inc) -> { val=11} mb_bind(mb_unit(“hello”), maybe_inc) -> “error” <- takes x and returns an object ( similar to unit) <- similar to bind – maybe simplified to: function(M, fn) { return fn(M.val)} <- inc() will not be executed and an error will be returned.
  14. 14. ANOTHER EXAMPLE- MAYBE (CONT’D) 1. Composition ( case: valid argument) mb_bind(mb_bind(mb_unit(10),maybe_inc) , maybe_dbl) -> mb_bind({mb_bind({val=10}, maybe_inc), maybe_dbl) -> mb_bind({val=11}, maybe_dbl) -> {val=22} 2. Composition: ( case: invalid argument) var a; // undefined mb_bind(mb_bind(mb_unit(a),maybe_inc), maybe_dbl) -> mb_bind(“Error”, maybe_inc), maybe_dbl) -> mb_bind({“Error” maybe_dbl) -> „ErrorR As you can see using this technique function composition can be achieved in a simple but powerful manner. If one passes a bad argument the mb_bind function will continue executing without throwing an exception.
  15. 15. FINALLY.. – One may have noticed that we have not modified the original inc() and db() functions – Composition allows one to build new functions from existing functions – The unit/bind mechanism described here allows you to compose functions which are not symmetric as far their argument and return value types are concerned. – Higher-order functions, composition and the unit/bind mechanism allows one to manage side effects in a systematic manner and also manage complexity as the software size increases.