23
Haskell, Scala, F# Are they worth the fuss?

The Fuss about || Haskell | Scala | F# ||

Embed Size (px)

Citation preview

Page 1: The Fuss about || Haskell | Scala | F# ||

Haskell, Scala, F#Are they worth the fuss?

Page 2: The Fuss about || Haskell | Scala | F# ||

My Background• B.Tech. IIT-Bombay. Computer Science.

• Ph.D. USC. Algorithmic Algebra.

• VP. Goldman Sachs. Trading Strategist.

• Managing Director. Morgan Stanley.

• Founder & C.E.O. ZLemma (A Tech Startup).

Page 3: The Fuss about || Haskell | Scala | F# ||

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!

Page 4: The Fuss about || Haskell | Scala | F# ||

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

Page 5: The Fuss about || Haskell | Scala | F# ||

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 …

Page 6: The Fuss about || Haskell | Scala | F# ||

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

Page 7: The Fuss about || Haskell | Scala | F# ||

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

Page 8: The Fuss about || Haskell | Scala | F# ||

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?

Page 9: The Fuss about || Haskell | Scala | F# ||

let notFactorOf n = fun i -> n % i <> 0 let intSqrt n = (int << sqrt << float) nlet 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#

Page 10: The Fuss about || Haskell | Scala | F# ||

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

Page 11: The Fuss about || Haskell | Scala | F# ||

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”?

Page 12: The Fuss about || Haskell | Scala | F# ||

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”?

Page 13: The Fuss about || Haskell | Scala | F# ||

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

Page 14: The Fuss about || Haskell | Scala | F# ||

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.

Page 15: The Fuss about || Haskell | Scala | F# ||

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#

Page 16: The Fuss about || Haskell | Scala | F# ||

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)

Page 17: The Fuss about || Haskell | Scala | F# ||

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!

Page 18: The Fuss about || Haskell | Scala | F# ||

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)

Page 19: The Fuss about || Haskell | Scala | F# ||

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!

Page 20: The Fuss about || Haskell | Scala | F# ||

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)

Page 21: The Fuss about || Haskell | Scala | F# ||

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)

Page 22: The Fuss about || Haskell | Scala | F# ||

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)

Page 23: The Fuss about || Haskell | Scala | F# ||

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)