"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
Function composition in Javascript
1.
2. 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.
3. 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)
4. 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)))}
5. 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.
6. 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
7. 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?
8. 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()”
}
9. 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.
10. 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.
11. 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
12. 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)
13. 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";
}
{
14. 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.
15. 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.
16. 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.