functional programming for oo programmers (part 2)

79
FUNCTIONAL PROGRAMMING for OO programmers (Part 2)

Upload: calvin-cheng

Post on 14-Jul-2015

896 views

Category:

Software


1 download

TRANSCRIPT

Page 1: Functional Programming for OO Programmers (part 2)

FUNCTIONAL PROGRAMMINGfor OO programmers (Part 2)

Page 2: Functional Programming for OO Programmers (part 2)

WHAT

• Pure versus Impure (exercise)

• Currying (exercise)

• Map, Filter, Reduce (exercise)

• Functors, Applicative Functors, Monads (exercise)

are we diving into?

Page 3: Functional Programming for OO Programmers (part 2)

PURE OR IMPURErecognize pure functions

Page 4: Functional Programming for OO Programmers (part 2)

RECOGNIZE

• Functions that don’t change anything out-of-scope and don’t depend on anything out-of-scope are called “pure”

• A pure function always gives the same result given the same parameters; independent of program/system state

pure functions

Page 5: Functional Programming for OO Programmers (part 2)

RECOGNIZEpure function

var  values  =  {  a:  1  };  

function  pureFunction  (a)  {  

   var  b  =  1;  

   a  =  a  *  b  +  2;  

   return  a;  

}  

Page 6: Functional Programming for OO Programmers (part 2)

RECOGNIZEimpure function

var  values  =  {  a:  1  };  

function  impureFunction  (items)  {  

   var  b  =  1;  

   items.a  =  items.a  *  b  +  2;  

   return  items;  

}  

Page 7: Functional Programming for OO Programmers (part 2)

LET’S PLAYpure or impure?

Page 8: Functional Programming for OO Programmers (part 2)

PURE OR IMPURE?function  getQueryVariable(variable)  {  

   var  query  =  window.location.search.substring(1);  

   var  vars  =  query.split('&');  

   for  (var  i  =  0;  i  <  vars.length;  i++)  {  

           var  pair  =  vars[i].split('=');  

           if  (decodeURIComponent(pair[0])  ===  variable)  {  

                       return  decodeURIComponent(pair[1]);  

                   }  

       }  

}

Page 9: Functional Programming for OO Programmers (part 2)

PURE OR IMPURE?function  random(mZ,  mW)  {  

   mZ  =  36969  *  (mZ  &  65535)  +  (mZ  >>  16);  

   mW  =  18000  *  (mW  &  65535)  +  (mW  >>  16);  

   return  (mZ  >>  16)  +  mW;  

}  

Page 10: Functional Programming for OO Programmers (part 2)

PURE OR IMPURE?function  addAndShow(a,  b,  console)  {  

   var  c  =  a  +  b;  

   console.log(a,  '  +  ',  b,  '  =  ',  c);  

   return  c;  

};  

Page 11: Functional Programming for OO Programmers (part 2)

PURE OR IMPURE?var  number  =  1;  

var  increment  =  function()  {  

   return  number  +=  1;  

};  

increment();  

Page 12: Functional Programming for OO Programmers (part 2)

PURE OR IMPURE?var  number  =  1;  

var  incrementAlt  =  function(n)  {  

       return  n  +  1;  

};  

incrementAlt(number);

Page 13: Functional Programming for OO Programmers (part 2)

CURRYINGfirst-order function and higher-order function capabilities

Page 14: Functional Programming for OO Programmers (part 2)

CURRYINGvar  people  =  [  

   {  name:  'Calvin'  },  

   {  name:  'John'  },  

   {  name:  'Thomas'  }  

];  

//  function  with  hardcoded  key  'name'  to  retrieve  list  of  names  

function  getPersonName(obj)  {  

   return  obj['name'];  

}  

//  mapping  to  the  hardcoded  function  

var  names  =  people.map(getPersonName);  

console.log(names);

JavaScript

Page 15: Functional Programming for OO Programmers (part 2)

CURRYING//  Specify  the  key  on-­‐the-­‐fly  by  using  a  generic  function.  

function  getByKey(key,  obj)  {  

   return  function(obj)  {  

       return  obj[key];  

   };  

}  

JavaScript

Page 16: Functional Programming for OO Programmers (part 2)

CURRYINGfunction  getByKey(key,  obj)  {  

   return  function(obj)  {  

       return  obj[key];  

   };  

}  

var  getByKeyPartial  =  getByKey('name');  

console.log(getByKeyPartial);  

var  names2  =  people.map(getByKeyPartial);  

console.log(names2);  

[Function]

curried function or partial function

JavaScript

Page 17: Functional Programming for OO Programmers (part 2)

CURRYING

function  getByKey(key,  obj)  {  

   return  function(obj)  {  

       return  obj[key];  

   };  

}  

var  names3  =  people.map(getByKey('name'));  

console.log(names3);

[Function]curried function or partial functionequivalent to:getByKey(‘name’)(obj) in this context

JavaScript

Page 18: Functional Programming for OO Programmers (part 2)

CURRYINGmodule  Main  where  

f  ::  Integer  

f  =  max  4  5  

fPartial  ::  (Ord  a,  Num  a)  =>  a  -­‐>  a    

fPartial  =  max  4  

main  ::    IO  ()  

main  =  do  

   print  f    

   let  g  =  fPartial  10  

   print  g

Haskell

Page 19: Functional Programming for OO Programmers (part 2)

CURRYINGpeople  ::    [(String,  String)]  

people  =  [("name",  "Calvin")  

       ,("wat",  "John")  

       ,("name",  "Thomas")  

       ]  

Haskell

Page 20: Functional Programming for OO Programmers (part 2)

CURRYING{-­‐  generic  filterByKey  function  that  accepts  a  string  as  

keyname  and  the  data  structure  shown  previously  -­‐}  

filterByKey  ::  Eq  a  =>  a  -­‐>  [(a,  t)]  -­‐>  [t]  

filterByKey  _  []  =  []  

filterByKey  p  ((k,  v):xs)  

       |  p  ==  k        =  v  :  filterByKey  p  xs  

       |  otherwise  =  filterByKey  p  xs  

filterByName  ::  [(String,  t)]  -­‐>  [t]  

filterByName  =  filterByKey  "name"

Haskell

[Function]curried function or partial function

Page 21: Functional Programming for OO Programmers (part 2)

CURRYINGmain  ::    IO  ()  

main  =  do  

       let  names2  =  filterByKey  "name"  people  

       print  names2  

       let  names3  =  filterByName  people  

       print  names3  

Haskell

[Function]curried function or partial function

Page 22: Functional Programming for OO Programmers (part 2)

MAP, FILTER, REDUCELook ma, no loops

Page 23: Functional Programming for OO Programmers (part 2)

MAP, FILTER, REDUCE//  map,  reduce  and  filter  are  built-­‐in  as  methods  of  the    

//  Array  class  in  JS  

var  aList  =  [0,  1,  2];  

var  newList  =  aList.map(function  (i)  {  

   return  i  +  1;  

});  

console.log(newList);  

console.log(aList);

JavaScript map

Page 24: Functional Programming for OO Programmers (part 2)

MAP, FILTER, REDUCEaList  ::    [Integer]  

aList  =  [0,  1,  2]  

addOne  ::  [Integer]  

addOne  =  map  (+1)  aList  

Haskell map

Page 25: Functional Programming for OO Programmers (part 2)

MAP, FILTER, REDUCE//  map,  reduce  and  filter  are  built-­‐in  as  methods  of  the    

//  Array  class  in  JS  

var  aList  =  [0,  1,  2];  

var  lessThanTwo  =  aList.filter(function  (i)  {  

   return  i  <  2;  

});  

console.log(lessThanTwo);  

console.log(aList);

JavaScript filter

Page 26: Functional Programming for OO Programmers (part 2)

MAP, FILTER, REDUCEaList  ::    [Integer]  

aList  =  [0,  1,  2]  

lessThanTwo  ::  [Integer]  

lessThanTwo  =  filter  (<2)  aList  

Haskell filter

Page 27: Functional Programming for OO Programmers (part 2)

MAP, FILTER, REDUCE//  map,  reduce  and  filter  are  built-­‐in  as  methods  of  the    

//  Array  class  in  JS  

var  aList  =  [0,  1,  2];  

var  reduceToSum  =  aList.reduce(function  (a,  b)  {  

   return  a  +  b;  

});  

console.log(reduceToSum);  

console.log(aList);  

reduce

Page 28: Functional Programming for OO Programmers (part 2)

MAP, FILTER, REDUCEaList  ::    [Integer]  

aList  =  [0,  1,  2]  

reduceToSum  ::  Integer  

reduceToSum  =  foldl  (+)  0  aList  

Haskell reduce (foldl and foldr)

Page 29: Functional Programming for OO Programmers (part 2)

FUNCTORSa function that, given a value and a function, does the right thing

Page 30: Functional Programming for OO Programmers (part 2)

FUNCTORSfunction  addOne(value)  {  

   return  value  +  1;  

}  

console.log(addOne(10));    //  11  

function  addTwo(value)  {  

   return  value  +  2;  

}  

console.log(addTwo(10));    //  12  

JavaScript

Page 31: Functional Programming for OO Programmers (part 2)

FUNCTORS//  A  not-­‐quite-­‐there  Functor  

function  aFunctor(value,  fn)  {  

   return  fn(value);  

}  

console.log(aFunctor(10,  addOne));  //  11,  works  as  expected  

console.log(aFunctor([1,  2,  3],  addOne));    //  '1,2,31'  is  

returned,  which  is  not  what  we  want  

JavaScript

Page 32: Functional Programming for OO Programmers (part 2)

FUNCTORSfunction  betterFunctor(value,  fn)  {  

   if  (typeof  value  ===  'number')  {  

       return  fn(value);    

   }  else  {  

       var  map  =  Array.prototype.map;  

       return  map.call(value,  fn);  

   }  

}  

JavaScript

Page 33: Functional Programming for OO Programmers (part 2)

FUNCTORSconsole.log(betterFunctor([1,  2,  3],  addOne));    //  [2,  3,  

4]  is  what  we  expected  

console.log(betterFunctor(10,  addOne));                  //  11  is  

what  we  expected  

JavaScript

Page 34: Functional Programming for OO Programmers (part 2)

FUNCTORS//  JavaScript's  Array's  map  method  is  a  functor!  :-­‐)  

var  map  =  Array.prototype.map;  

console.log(map.call([1,  2,  3],  addOne));              //  [2,  3,  

4]  is  what  we  expected.    

console.log([].map.call([1,  2,  3],  addOne));        //  This  

works  too  

JavaScript

Page 35: Functional Programming for OO Programmers (part 2)

FUNCTORSaddOne  ::    Num  a  =>  a  -­‐>  a  

addOne  a  =  a  +  1  

addTwo  ::    Num  a  =>  a  -­‐>  a  

addTwo  a  =  a  +  2  

result  ::    Num  b  =>  [b]  -­‐>  [b]  

result  xs  =    map  addOne  xs  

Haskell

Page 36: Functional Programming for OO Programmers (part 2)

COMPARISONFunctors vs Applicative Functors vs Monads

Functors Applicatives Monads

example (+3) [2]== [5]

Just(+3) <*>

Just 2== Just 5

Just 4>>=

makeHalf== Just 2

examples functions map, fmap, <*> <*>, <$>, liftA2 >>=, liftM

brings function operator in

execute operationgeneralizedmap

does both <*> & <$>

apply fn to wrapped value

apply wrapped fn to wrapped value

apply fn that returns a wrapped value

to a wrapped value

Page 37: Functional Programming for OO Programmers (part 2)

MAYBE FUNCTORSan often-used functor; and more about functors

Page 38: Functional Programming for OO Programmers (part 2)

MAYBE FUNCTORS• Captures null check

• Value inside may or may not be there

• Maybe has two subclasses - ‘Just’ or ‘Nothing’ (Haskell)

• Also referred to as Option with subclasses ‘Some’ or ‘None’ (Scala)

• Also available in Swift, e.g. Optional, enum Either<NSError, User>

Page 39: Functional Programming for OO Programmers (part 2)

MAYBE FUNCTORSvar  aList  =  [1,  2,  3];  

function  compose(f,  g)  {  

   return  function  (x)  {  

       return  f(g(x));  

   };  

}  

function  addOne(value)  {  

   return  value  +  1;  

}  

function  addTwo(value)  {  

   return  value  +  2;  

}

JavaScript

Page 40: Functional Programming for OO Programmers (part 2)

MAYBE FUNCTORSconsole.log(aList.map(compose(addOne,  addTwo)));  

console.log(aList.map(addTwo).map(addOne));  

function  mayBe(value,  fn)  {  

   return  value  ===  null  ||  value  ===  undefined  ?  value  :  

fn(value);  

}  

JavaScript

Page 41: Functional Programming for OO Programmers (part 2)

MAYBE FUNCTORSconsole.log(mayBe(undefined,  compose(addOne,  addTwo)));          

//  returns  expected  result  undefined  

console.log(mayBe(mayBe(undefined,  addTwo),  addOne));              

//  returns  expected  result  undefined  

console.log(mayBe(1,  compose(addOne,  addTwo)));                          

//  returns  expected  result  4    

console.log(mayBe(mayBe(1,  addTwo),  addOne));                              

//  returns  expected  result  4  

JavaScript

Page 42: Functional Programming for OO Programmers (part 2)

MAYBE FUNCTORSaddOne  ::    Num  a  =>  a  -­‐>  a  

addOne  a  =  a  +  1  

addTwo  ::    Num  a  =>  a  -­‐>  a  

addTwo  a  =  a  +  2  

composedFn  ::    Integer  -­‐>  Integer  

composedFn  =  addOne  .  addTwo  

res  ::    [Integer]  

res  =  map  composedFn  aList

Haskell

Page 43: Functional Programming for OO Programmers (part 2)

MAYBE FUNCTORSmain  ::    IO  ()  

main  =  do  

       print  res  

       let  res2  =  fmap  composedFn  Nothing  

       print  res2  

       let  res3  =  fmap  composedFn  (Just  1)  

       print  res3

Haskell

Page 44: Functional Programming for OO Programmers (part 2)

APPLICATIVESapply wrapped function to wrapped value

Page 45: Functional Programming for OO Programmers (part 2)

APPLICATIVESvar  none  =  {  

       map:  function()  {  

               return  none;  

       },  

       bind:  function()  {  

               return  none;  

       },  

         

       toString:  function()  {  

               return  'none';  

       }  

};

JavaScript

Page 46: Functional Programming for OO Programmers (part 2)

APPLICATIVESfunction  some(value)  {  

       return  {  

               map:  function(func)  {  

                       return  some(func(value));  

               },  

                 

               bind:  function(func)  {  

                       return  func(value);  

               },  

                 

               toString:  function()  {  

                       return  "some("  +  value  +  ")";  

               }  

       };  

}  

JavaScript

Page 47: Functional Programming for OO Programmers (part 2)

APPLICATIVESvar  functor  =  {  

       map:  function(func,  option)  {  

               return  option.map(func);  

       },  

       unit:  some,  

       applyFunctor:  function(funcOption,  argOption)  {  

               return  funcOption.bind(function(func)  {  

                       return  argOption.map(func);  

               });  

       }  

};

JavaScript

Page 48: Functional Programming for OO Programmers (part 2)

APPLICATIVESfunction  curry(func,  numberOfArguments)  {  

   return  function(value)  {  

       if  (numberOfArguments  ===  1)  {  

           return  func(value);  

       }  else  {  

           return  curry(func.bind(null,  value),  numberOfArguments  -­‐  1);  

       }  

   };  

}

JavaScript

Page 49: Functional Programming for OO Programmers (part 2)

APPLICATIVES//  Usage  

var  four  =  some(4);  

var  six  =  some(6);  

console.log(four.toString());  

console.log(six.toString());  

function  add(a,  b)  {  

   return  a  +  b;  

}  

var  result  =  functor.applyFunctor(functor.map(curry(add,  2),  four),  six);  

console.log(result.toString());  //  some(10)

JavaScript

Page 50: Functional Programming for OO Programmers (part 2)

APPLICATIVESresult  =  functor.applyFunctor(functor.map(curry(add,  2),  

none),  six);  

console.log(result.toString());  

result  =  functor.applyFunctor(functor.map(curry(add,  2),  

four),  none);  

console.log(result.toString());

JavaScript

Page 51: Functional Programming for OO Programmers (part 2)

APPLICATIVES//  A  cleaner  API  for  our  applicative  functor  operations  

functor.applyFunctorUncurried  =  function(func)  {  

   var  args  =  Array.prototype.slice.call(arguments,  1);  

   return  args.reduce(  

       functor.applyFunctor,  

       functor.unit(curry(func,  args.length))  

   );  

};  

JavaScript

Page 52: Functional Programming for OO Programmers (part 2)

APPLICATIVESvar  result2  =  functor.applyFunctorUncurried(add,  four,  six);  

console.log(result2.toString());  

result2  =  functor.applyFunctorUncurried(add,  none,  six);  

console.log(result2.toString());  

result2  =  functor.applyFunctorUncurried(add,  four,  none);  

console.log(result2.toString());  

JavaScript

Page 53: Functional Programming for OO Programmers (part 2)

APPLICATIVESimport  Control.Applicative  

ans  ::    Maybe  Integer  

ans  =  (+)  <$>  Just  4  <*>  Just  6  

main  ::    IO  ()  

main  =  print  ans  

Haskell

Page 54: Functional Programming for OO Programmers (part 2)

MONADSapply function that returns wrapped value to a wrapped value

Page 55: Functional Programming for OO Programmers (part 2)

MONAD“A monad is just a monoid in the category of endofunctors, what's the problem?”

- James Iry (Brief, Incomplete and Mostly Wrong History of Programming Languages)

Page 56: Functional Programming for OO Programmers (part 2)

MONADLet’s

• understand the why,

• then, illustrate the how

• and, we will know what a monad is.

Page 57: Functional Programming for OO Programmers (part 2)

MONADWhy?

avoid mutable state while chaining a group of functions together (or executing a sequence of logic)

Page 58: Functional Programming for OO Programmers (part 2)

MONADmutation:

functions in imperative languages change the state of values as logic is executed

Page 59: Functional Programming for OO Programmers (part 2)

MONADSo what’s the big deal?

When data is mutable, parallel thread execution of your code on multiple CPUs leads to race conditions

Can’t do parallel thread execution on your multi-core machine ⇒

idle resource

If we insist on parallel thread execution without locks ⇒

unpredictable results, i.e. errors

Page 60: Functional Programming for OO Programmers (part 2)

MONAD

Page 61: Functional Programming for OO Programmers (part 2)

MONAD f (x)

x = g(x)

x = h(x)

x = i(x)

return x

Page 62: Functional Programming for OO Programmers (part 2)

MONAD f (x)

x = g(x)

x = h(x)

x = i(x)

return x

x = 3

x = 5

x = 10

x = -3 x = -3

Page 63: Functional Programming for OO Programmers (part 2)

f (x)

x = g(x)

x = h(x)

x = i(x)

return x

f (x)

x = g(x)

x = h(x)

x = i(x)

return x

MONAD x = 3

x = 5

x = 17

x = 4 x = -9

x = 7

x = 12

CPU #1 CPU #2

x = 4 x = -9

Shared Memory, x

Page 64: Functional Programming for OO Programmers (part 2)

MONADAs you can see, the same function results in indeterminate results depending on the whims of the parallel OS-controlled POSIX threads (“pthreads”)

We expect that when we write the code (“computation logic”) for f(x), when given x = 3, f(x) will always result in -3 but because x is mutable in traditional languages, this is not the case if we try to make our program run on multiple CPUs.

Page 65: Functional Programming for OO Programmers (part 2)

MONADImperative languages that we are familiar with solve this problem with

• locks (semaphores) on POSIX threads; or

• green threads (concurrent threads) and asynchronous I/O

Page 66: Functional Programming for OO Programmers (part 2)

MONADHaskell has everything + more performant approaches:

• sparks (super-duper lightweight green threads)

• green threads

• semaphores

• immutable variables by default, “side effects” (mutability) achieved via monads

Page 67: Functional Programming for OO Programmers (part 2)

f (x)

x1 = g(x)

x2 = h(x1)

x3 = i(x2)

return x3

f (x)

x1 = g(x)

x2 = h(x1)

x3 = i(x2)

return x3

MONAD x = 3

x1 = 5

x2 = 10

x3 = -3 x3 = -3

x1 = 5

x2 = 10

CPU #1 CPU #2

x3 = -3 x3 = -3

Page 68: Functional Programming for OO Programmers (part 2)

MONADHaskell

f :: Num a => a -> a

f x = let

x1 = x + 2

x2 = x1 + 5

x3 = x2 - 13

in x3

f :: Num a => a -> a

f x = let

x = x + 2

x = x + 5

x = x - 13

in x

error :variables in haskell

are immutable values!no side effects!

Page 69: Functional Programming for OO Programmers (part 2)

MONADSo, now, we know the Why?

avoid mutable state while chaining a group of functions together (or executing a sequence of logic)

So, how can a monad achieve the magic illustrated in the previous 2 slides?

Page 70: Functional Programming for OO Programmers (part 2)

MONADHow?

• type container

• return

• bind

Page 71: Functional Programming for OO Programmers (part 2)

MONADvar  Maybe  =  function(value)  {  //  container  

   this.value  =  value;  

};  

Maybe.prototype.ret  =  function()  {    //  return  

   return  this.value;  

};  

Maybe.prototype.bind  =  function(func)  {    //  bind  

   if  (this.value  !==  null)  {  

       return  func(this.value);  

   }  

   return  this.value;  

};

JavaScript

Page 72: Functional Programming for OO Programmers (part 2)

MONAD//  lift:  takes  in  a  function  that  returns  a  normal  value  and  changes  it  in  a  monad  

Maybe.lift  =  function(func)  {  

   return  function(value)  {  

       return  new  Maybe(func(value));  

   };  

};  

//  Usage  

var  addOne  =  function(value)  {  

   return  value  +  1;  

};  

//  we  can  use  this  with  bind  

var  maybeAddOne  =  Maybe.lift(addOne);

JavaScript

Page 73: Functional Programming for OO Programmers (part 2)

MONAD//  lift2  use  closures  to  get  values  from  the  two  monads    

//  before  running  it  through  function,  handling  the  undefined  cases  

Maybe.lift2  =  function(func)  {  

   return  function(M1,  M2)  {  

       return  new  Maybe(M1.bind(function(value1){  

           return  M2.bind(function(value2)  {  

               return  func(value1,  value2);  

           });  

       }));  

   };  

};

JavaScript

Page 74: Functional Programming for OO Programmers (part 2)

MONADvar  add  =  function(a,  b)  {return  a  +  b;};  

var  m1  =  new  Maybe(1);  

var  m2  =  new  Maybe(2);  

var  m3  =  new  Maybe(undefined);  

var  liftM2Add  =  Maybe.lift2(add);  

liftM2Add(m1,  m2).ret();  //3  

liftM2Add(m3,  m2).ret();  //undefined  

liftM2Add(m1,  m3).ret();  //undefined  

JavaScript

Page 75: Functional Programming for OO Programmers (part 2)

MONADa  ::    Maybe  Integer  

a  =  Just  1  

f  ::    Integer  -­‐>  Maybe  Integer  

f  =  \x  -­‐>  Just  (x  +  1)  

main  ::    IO  ()  

main  =  do  

   let  ans  =  a  >>=  f  

   print  ans    {-­‐  we  expect  to  get  Just  2  -­‐}

Haskell

Page 76: Functional Programming for OO Programmers (part 2)

MONAD• type container

• return

• bind

• We pass in function(s) to operate values inside it

• and get a returned value

Page 77: Functional Programming for OO Programmers (part 2)

MONADThe code

• http://github.com/calvinchengx/learnhaskell

Page 78: Functional Programming for OO Programmers (part 2)

THANK YOU

@calvinchengx

calvin.cheng.lc

calvinchengx

calvinx.com

Page 79: Functional Programming for OO Programmers (part 2)

CRAFTSMANSHIPMaster your craft - Procedural, OO, Functional