3. Haskell, Scala, F#
• A different way of thinking about coding
• Functional Programming is just one feature
• High productivity (not easy to produce bugs)
• Elegant and expressive (readable, maintainable)
• Enjoyable and Addictive!
4. A different way of thinking
• The way we think when we do Math
• It’s not about objects, state and “recipes”
• It’s about (in Math-speak) sets, functions, compositions
• Express what you want to do, not “how to do”
• Elite programmers are influencing a cultural shift
5. More than just FP
• FP means stateless code & higher-order functions
• {Haskell, Scala, F#} -> many other nice things
• Static but Inferred Typing. Hence, safe yet succinct
• Algebraic Data Types & Pattern Matching
• The M word …
6. Stateless code
• Everything is a const. Banish the word “variable”
• So: no loops, no reassignments, no object edits
• Instead: map, fold, recursion, algebraic data types
• Many small functions delegating to other functions
• “State is the root cause of bugs” #MadeUpQuote
7. def isprime(n):
if n == 2:
return True
if n == 1 or n % 2 == 0:
return False
max = n**0.5+1
i = 3
while i <= max:
if n % i == 0:
return False
i+=2
return 1
Python done badly
8. def isPrime(n):
if n == 1:
return False
else:
return all(n % i != 0 for i in range(2, int(sqrt(n)) + 1) if isPrime(i))
Python done well
But what about typing?
9. let notFactorOf n =
fun i -> n % i <> 0
let intSqrt n =
(int << sqrt << float) n
let rec isPrime = function
| 1 -> false
| n -> Seq.forall (notFactorOf n) (seq {for i in 2 .. (intSqrt n)
do if isPrime i then yield i})
Same code in F#
10. Higher-order Functions
• Think of functions as a “generalization of data”
• Functions accepting and/or returning function
• Function composition (chaining)
• Exploit the power of lambdas and currying
• Laziness and memoization
11. class NumSet(object):
def __init__(self):
self.__data__ = None
def __calc__(self):
return self
def get_calc(self):
if not self.__data__:
self.__data__ = self.__calc__()
return self.__data__
class NumSetLeaf(NumSet):
def __init__(self, low, mid, high):
super(NumSetLeaf, self).__init__()
self.low = low
self.mid = mid
self.high = high
self.triple = (self.low, self.mid, self.high)
class NumSetOp(NumSet):
def __init__(self, fl, args):
super(NumSetOp, self).__init__()
self.fl = fl
self.args = args
def __calc__(self):
lows, mids, highs = zip(*[x.get_calc().triple for x in self.args])
fl = self.fl
return NumSetLeaf(fl(lows), fl(mids), fl(highs))
class NumSetSum(NumSetOp):
def __init__(self, args):
super(NumSetSum, self).__init__(sum, args)
Can Python “Haskell”?
12. class NumSetUnaryOp(NumSetOp):
def __init__(self, f1, arg):
super(NumSetUnaryOp, self).__init__(lambda l1, f1=f1: f1(*l1), [arg])
self.f1 = f1
self.arg = arg
class NumSetScale(NumSetUnaryOp):
def __init__(self, arg, k):
super(NumSetScale, self).__init__(lambda x, k=k: x * k, arg)
self.k = k
class NumSetBinaryOp(NumSetOp):
def __init__(self, f2, arg1, arg2):
super(NumSetBinaryOp, self).__init__(lambda l2, f2=f2: f2(*l2), [arg1, arg2])
self.f2 = f2
self.arg1 = arg1
self.arg2 = arg2
class NumSetPlus(NumSetBinaryOp):
def __init__(self, arg1, arg2):
super(NumSetPlus, self).__init__(lambda x, y: x + y, arg1, arg2)
class NumSetMult(NumSetBinaryOp):
def __init__(self, arg1, arg2):
super(NumSetMult, self).__init__(lambda x, y: x * y, arg1, arg2)
l1 = NumSetLeaf(0.9, 0.92, 0.95
l2 = NumSetLeaf(0.8, 0.85, 0.9)
l3 = NumSetLeaf(0.6, 0.7, 0.8)
k = 0.6
l4 = NumSetPlus(NumSetScale(l1, k), NumSetScale(l2, 1.0 - k))
l5 = NumSetScale(NumSetSum([l4, l2, l3]), 0.3)
Can Python “Haskell”?
13. Infered Typing
• The best of both worlds
The rigor and safety of static typing
Type inference lends syntactic lightness
• Most coding errors are caught as one types code
• Visualization of typing makes one think better
• Scripting-style, rapid prototyping, REPL
14. Algebraic Data Types
• Elegant (recursive) way to express data structures
• Makes writing of algorithms very easy and natural
• Pattern-matching on edge cases and types
• No more ugly/dangerous if-elseif, switch/case code
• Type/structural safety. Hard to introduce bugs.
15. type color = R | B
type 'a tree =
| E
| T of color * 'a tree * 'a * 'a tree
module Tree =
let hd = function
| E -> failwith "empty"
| T(c, l, x, r) -> x
let left = function
| E -> failwith "empty"
| T(c, l, x, r) -> l
let right = function
| E -> failwith "empty"
| T(c, l, x, r) -> r
let rec exists item = function
| E -> false
| T(c, l, x, r) ->
if item = x then true
elif item < x then exists item l
else exists item r
let balance = function (* Red nodes in relation to black root *)
| B, T(R, T(R, a, x, b), y, c), z, d (* Left, left *)
| B, T(R, a, x, T(R, b, y, c)), z, d (* Left, right *)
| B, a, x, T(R, T(R, b, y, c), z, d) (* Right, left *)
| B, a, x, T(R, b, y, T(R, c, z, d)) (* Right, right *)
-> T(R, T(B, a, x, b), y, T(B, c, z, d))
| c, l, x, r -> T(c, l, x, r)
let insert item tree =
let rec ins = function
| E -> T(R, E, item, E)
| T(c, a, y, b) as node ->
if item = y then node
elif item < y then balance(c, ins a, y, b)
else balance(c, a, y, ins b)
match ins tree with
| E -> failwith "Should never return empty from an insert"
| T(_, l, x, r) -> T(B, l, x, r)
Red-Black Tree in F#
16. Make Data, Not Code
• Data-driven coding (parametric algorithms)
• Structured data framework controls your algorithms
• A system where key logic is in data, not code
• User wants a lot of control => Give him a DSL
• Plug & Play system => Bring Your Own Data (DSL)
17. DSL
• Internal versus External
• External: End-User DSL versus Dev DSL
• Internal: Syntactic Sugar for clean, controlled code
• Graphical representation of DSL => Functional
• Well-typed, Functional DSL => Happy Algorithms!
18. The M Word …
• Monads - not just for “side-effects computing”
• Functional way of doing imperative/sequential logic
• Define Domains, Ranges, Compositions (Chains)
• Sounds complex but yields clean, bug-free code
• 4 types of X => M(X) monadic “Range Expansion”
• g: A -> M(B), f: B -> M(C). How to get f o g: A -> M(C)
19. 4 types of Monads
• Failure: A -> Maybe(B), B -> Maybe(C).
• Configurations: A -> Settings(B), B -> Settings(C).
• Uncertainty: A -> Power(B), B -> Power(C).
• Side-Effects: A -> IO(B), B -> IO(C)
• Just figure out the bind and unit methods for each
• Chain with bind and unit, and it’s all automatic!
20. type Maybe<‘a> =
| Just of 'a
| Nothing
let Return (x: 'a) : Maybe<'a> =
Just(x)
let Bind (mx: Maybe<'b>) (f: 'b -> Maybe<'c>) : Maybe<'c> =
match mx with
| Just(x) -> f x
| Nothing -> Nothing
Failure: “Maybe” monad
Maybe(X) is an algebraic data type that can be X or “Error”
g: A -> Maybe(B)
f: B -> Maybe(C)
How to produce f o g : A -> Maybe(C)
21. let Return (x: 'a) : (‘s -> ‘a) =
fun _ -> x
let Bind (mx: ’s -> ‘b) (f: 'b -> (’s -> ‘c)) : (’s -> ‘c) =
fun y -> f (mx y) y
Configurations: “Settings” monad
Settings(X) is a function from a “Settings” type to X
g: A -> Settings(B)
f: B -> Settings(C)
How to produce f o g : A -> Settings(C)
22. let Return (x: 'a) : Set<‘a> =
set [x]
let Bind (mx: Set<‘b>) (f: 'b -> Set<‘c>) : Set<‘c> =
set [for x in mx do yield! f x]
Uncertainty: “Power” monad
Power(X) is the power set of X (the set of all subsets of X)
g: A -> Power(B)
f: B -> Power(C)
How to produce f o g : A -> Power(C)
23. let Return (x: 'a) : (unit -> ‘a) =
fun () -> x
let Bind (mx: unit -> ‘b) (f: 'b -> unit -> ‘c) : (unit -> ‘c) =
f (mx())
Side-effects: “IO” monad
IO(X) is a side-effects-function from Null (i.e., no args) to X
g: A -> IO(B)
f: B -> IO(C)
How to produce f o g : A -> IO(C)