A monad in X is just a monoid in the category of endofunctors of X...

Philip Wadler (according to James Iry)

CAtegory

Functor

F:\ C \rightarrow D
F: CDF:\ C \rightarrow D
X \in C \Rightarrow F(X) \in D
XCF(X)DX \in C \Rightarrow F(X) \in D
f: X \rightarrow Y \Rightarrow F(f): F(X) \rightarrow F(Y)
f:XYF(f):F(X)F(Y)f: X \rightarrow Y \Rightarrow F(f): F(X) \rightarrow F(Y)
F(id_X) \equiv id_{F(X)}
F(idX)idF(X)F(id_X) \equiv id_{F(X)}
F(f \cdot g) \equiv F(f) \cdot F(g)
F(fg)F(f)F(g)F(f \cdot g) \equiv F(f) \cdot F(g)

Monoid

  • a set
  • an operation
  • an element
S
SS
\bullet: S \times S \rightarrow S
:S×SS\bullet: S \times S \rightarrow S
e: 1 \rightarrow S
e:1Se: 1 \rightarrow S
(a \bullet b) \bullet c = a \bullet (b \bullet c)
(ab)c=a(bc)(a \bullet b) \bullet c = a \bullet (b \bullet c)
e \bullet a = a = a \bullet e
ea=a=aee \bullet a = a = a \bullet e

Monad

  • an endofunctor
  • a transformation
  • a transformation
T: X \rightarrow X
T:XXT: X \rightarrow X
\mu: T \times T \rightarrow T
μ:T×TT\mu: T \times T \rightarrow T
\eta: I \rightarrow T
η:IT\eta: I \rightarrow T
\mu(\mu(T \times T) \times T) = \mu(T \times \mu(T \times T))
μ(μ(T×T)×T)=μ(T×μ(T×T))\mu(\mu(T \times T) \times T) = \mu(T \times \mu(T \times T))
\mu(\eta(T)) = T = \mu(T(\eta))
μ(η(T))=T=μ(T(η))\mu(\eta(T)) = T = \mu(T(\eta))

Imperative
>=>

Functional

Programming paradigms

Main paradigms

  • Imperative programming
  • Procedural programming
  • Object-Oriented programming
  • Declarative programming
  • Functional programming
  • Event-Driven programming
  • Automata-based programming

Imperative

Computation is number of statements that change a program state.

  • Assignments
  • Data structures
  • Global variables
  PROGRAM EUCLID
    PRINT *, 'A?'
    READ *, NA
    PRINT *, 'B?'
    READ *, NB
    PRINT *, 'GCD = ', GCD(NA, NB)
    STOP
  END

  FUNCTION GCD(NA, NB)
    IA = NA
    IB = NB
1   IF (IB.NE.0) THEN
      ITEMP = IA
      IA = IB
      IB = MOD(ITEMP, IB)
      GOTO 1
    END IF
    NGCD = IA
    RETURN
  END

Procedural / Structural

More logical program structure with concepts of modular programming and procedure calls.

  • Control structures:
    • Sequence
    • Selection
    • Iteration
  • Recursion
  • Subroutines
  • Blocks / Indentation
  • Modularity
// Go
func gcd(x, y int) int {
    for y != 0 {
        x, y = y, x%y
    }
    return x
}
// Rust
fn gcd(m: i32, n: i32) -> i32 {
   if m == 0 {
      n.abs()
   } else {
      gcd(n % m, m)
   }
}

Object-Oriented

Programs as objects: data structures that may contain “attributes” and “methods”. Object’s methods can access and modify object state.

  • Encapsulation
  • Inheritance
  • Polymorphism
  • Message passing
  • Data abstraction
public interface Map<K, V> {
    public Map<K, V> accept(Visitor<K, V> visitor);
}

public interface MapVisitor<K, V> {
    public V visit(K k, V v);
}

/**
 * Visitor that returns the GCD of k and v.
 */
public class GCD 
    implements MapVisitor<Integer, Integer> {
    public Integer visit(Integer k, Integer v) {
        if (v == 0) {
            return Math.abs(k);
        } else {
            return visit(v, k % v);
        }
    }
}

Declarative

Computational logic without explicit definition of control flow.

  • Logical statements
  • Proofs of statements
  • “What”, not “how”
  • Constraints
//Prolog
gcd(X, 0, X):- !.
gcd(0, X, X):- !.
gcd(X, Y, D):- X > Y, !, Z is X mod Y, gcd(Y, Z, D).
gcd(X, Y, D):- Z is Y mod X, gcd(X, Z, D).
-- SQL
SELECT customer_id, SUM (amount)
FROM payment
GROUP BY customer_id
HAVING SUM (amount) > 200;

FUNctional

Computation is an evaluation of functions avoiding state changing and data mutations.

  • Lambda calculus
  • First-class / Higher-order functions
  • Pure functions
  • Referential transparency
  • Lazy evaluation
  • Recursion
gcd :: (Integral a) => a -> a -> a
gcd 0 0 =  error "gcd 0 0 is undefined"
gcd x y =  gcd' (abs x) (abs y) where
  gcd' a 0  =  a
  gcd' a b  =  gcd' b (a `rem` b)

qsort :: (Ord a) => [a] -> [a]  
qsort [] = []
qsort (x:xs) = qsort ys ++ x : qsort zs where
    (ys, zs) = partition (< x) xs

λ calculus

Motivations: anonymity & currying

square\_sum(x, y) = x^2 + y^2 \Rightarrow (x, y) \mapsto x^2 + y^2
square_sum(x,y)=x2+y2(x,y)x2+y2square\_sum(x, y) = x^2 + y^2 \Rightarrow (x, y) \mapsto x^2 + y^2
(x, y) \mapsto x^2 + y^2 \Rightarrow x \mapsto (y \mapsto x^2 + y^2)
(x,y)x2+y2x(yx2+y2)(x, y) \mapsto x^2 + y^2 \Rightarrow x \mapsto (y \mapsto x^2 + y^2)

λ calculus

Informal definition

  • Terms:
    • variable
    • abstraction
    • application
\lambda x.\enspace x = \lambda y.\enspace y
λx.x=λy.y\lambda x.\enspace x = \lambda y.\enspace y
e_1\enspace e_2\enspace e_3 = (e_1\enspace e_2)\enspace e_3
e1e2e3=(e1e2)e3e_1\enspace e_2\enspace e_3 = (e_1\enspace e_2)\enspace e_3
\lambda x.\enspace x\enspace x = \lambda x.\enspace (x\enspace x) \ne (\lambda x.\enspace x)\enspace x
λx.xx=λx.(xx)(λx.x)x\lambda x.\enspace x\enspace x = \lambda x.\enspace (x\enspace x) \ne (\lambda x.\enspace x)\enspace x
\lambda y.\enspace \overline x \enspace(\lambda x.\enspace x\enspace x)\enspace \overline x
λy.x(λx.xx)x\lambda y.\enspace \overline x \enspace(\lambda x.\enspace x\enspace x)\enspace \overline x
  • “Free” variables
x
xx
\lambda x. \enspace t
λx.t\lambda x. \enspace t
t \enspace s
tst \enspace s

λ calculus

Reduction and recursion

  • Reductions
(\lambda x.\enspace e_1) \enspace e_2 \Rightarrow e_1 \enspace [ e_2 / x]
(λx.e1)e2e1[e2/x](\lambda x.\enspace e_1) \enspace e_2 \Rightarrow e_1 \enspace [ e_2 / x]
(\lambda x. \enspace x)(\lambda y. \enspace y) \Rightarrow \lambda y. \enspace y
(λx.x)(λy.y)λy.y(\lambda x. \enspace x)(\lambda y. \enspace y) \Rightarrow \lambda y. \enspace y
  • Y-combinator
(\lambda x. \enspace x \enspace x)(\lambda y. \enspace y) \Rightarrow (\lambda y. \enspace y)(\lambda y. \enspace y)
(λx.xx)(λy.y)(λy.y)(λy.y)(\lambda x. \enspace x \enspace x)(\lambda y. \enspace y) \Rightarrow (\lambda y. \enspace y)(\lambda y. \enspace y)
(\lambda x. \enspace x \enspace (\lambda x. \enspace x))(\lambda y. \enspace y) \Rightarrow y \enspace (\lambda x. \enspace x)
(λx.x(λx.x))(λy.y)y(λx.x)(\lambda x. \enspace x \enspace (\lambda x. \enspace x))(\lambda y. \enspace y) \Rightarrow y \enspace (\lambda x. \enspace x)
Y \equiv \lambda f. \enspace (\lambda x. \enspace f \enspace (x \enspace x)) \enspace (\lambda x. \enspace f \enspace (x \enspace x))
Yλf.(λx.f(xx))(λx.f(xx))Y \equiv \lambda f. \enspace (\lambda x. \enspace f \enspace (x \enspace x)) \enspace (\lambda x. \enspace f \enspace (x \enspace x))
fact \equiv \lambda n. \enspace cond \enspace (= \enspace n \enspace 0) \enspace 1 \enspace (\ast \enspace n \enspace (fact \enspace (- \enspace n \enspace 1))))
factλn.cond(=n0)1(n(fact(n1))))fact \equiv \lambda n. \enspace cond \enspace (= \enspace n \enspace 0) \enspace 1 \enspace (\ast \enspace n \enspace (fact \enspace (- \enspace n \enspace 1))))
fact \equiv Y \enspace ( \lambda f. \enspace \lambda n. \enspace cond \enspace (= \enspace n \enspace 0) \enspace 1 \enspace (\ast \enspace n \enspace (f \enspace (- \enspace n \enspace 1)))))
factY(λf.λn.cond(=n0)1(n(f(n1)))))fact \equiv Y \enspace ( \lambda f. \enspace \lambda n. \enspace cond \enspace (= \enspace n \enspace 0) \enspace 1 \enspace (\ast \enspace n \enspace (f \enspace (- \enspace n \enspace 1)))))

λ calculus

Y-combinator

Y \enspace g \equiv (\lambda f. \enspace (\lambda x. \enspace f \enspace (x \enspace x)) \enspace (\lambda x. \enspace f \enspace (x \enspace x))) \enspace g
Yg(λf.(λx.f(xx))(λx.f(xx)))gY \enspace g \equiv (\lambda f. \enspace (\lambda x. \enspace f \enspace (x \enspace x)) \enspace (\lambda x. \enspace f \enspace (x \enspace x))) \enspace g
Y \enspace g \equiv g \enspace (Y \enspace g)
Ygg(Yg)Y \enspace g \equiv g \enspace (Y \enspace g)
\equiv (\lambda x. \enspace g \enspace (x \enspace x)) \enspace (\lambda x. \enspace g \enspace (x \enspace x))
(λx.g(xx))(λx.g(xx))\equiv (\lambda x. \enspace g \enspace (x \enspace x)) \enspace (\lambda x. \enspace g \enspace (x \enspace x))
\equiv g \enspace ((\lambda x. \enspace g \enspace (x \enspace x)) \enspace (\lambda x. \enspace g \enspace (x \enspace x)))
g((λx.g(xx))(λx.g(xx)))\equiv g \enspace ((\lambda x. \enspace g \enspace (x \enspace x)) \enspace (\lambda x. \enspace g \enspace (x \enspace x)))
\equiv g \enspace (Y \enspace g)
g(Yg)\equiv g \enspace (Y \enspace g)

λ calculus

Functions

First-class functions are a necessity for the functional programming style, in which the use of higher-order functions is a standard practice.

  • First-class functions
    Passed, returned & assigned
map :: (a -> b) -> [a] -> [b]
map f []     = []
map f (x:xs) = f x : map f xs

main = map (\x -> 3 * x + 1) [1, 2, 3, 4, 5]

f :: [[Integer] -> [Integer]]
f = let a = 3
        b = 1
     in [ map (\x -> a * x + b)
        , map (\x -> b * x + a) ]
  • Higher-order functions
    Take functions as arguments
    May return functions

Pure Functions

Always evaluate the same result value given the same argument values. Also, evaluation of the result does not cause any semantically observable side effect or output.

  • Pure functions
  • Memoization
  • Thread safety
  • Rearrangement
  • Removal
// Pure
def sin(x: Double) = math.sin(x)
def length(s: String) = s.length()

// Impure
def inc(x: Int) = x + a
def random() = util.Random().nextDouble()
def printf(fmt: String, str: String): Unit = {
  println(fmt format str)
}

// Pure, again
def random(seed: Int) = util.Random(seed).nextDouble()
def printf(fmt: String, str: String): IO[Unit] = {
  for {
    _ <- putStrLn(fmt format str)
  } yield ()
}

Lazy and Recursive

Delays the evaluation of an expression until its value is needed and avoids repeated evaluations.

  • Referential transparency
  • Abstract control flow
  • Infinite data structures
foldl :: (a -> a -> a) -> a -> [a] -> a
foldl f z []     = z
foldl f z (x:xs) = foldl f (f z x) xs

foldr :: (a -> a -> a) -> a -> [a] -> a
foldr f z []     = z
foldr f z (x:xs) = f x (foldr f z xs)
seq    ::  a -> b -> b
foldl' :: (a -> a -> a) -> a -> [a] -> a

foldl' f z []     = z
foldl' f z (x:xs) = let z' = f z x 
                    in seq z' $ foldl' f z' xs
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

fibs :: Num a => [a]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

Cons

If you good at something, never do it for free...

  • We need state :(
    • a -> IO ()
    • State a -> (a, State a)
    • [Request] -> [Response]
  • Efficiency?
    • GC, allocating much?
    • arrays: O(1) vs O(log n)
    • too lazy?
  • Collections
    • mutable
    • weak keys
    • concurrency
    • graphs
qsort :: (Ord a) => [a] -> [a]  
qsort [] = []
qsort (x:xs) = qsort ys ++ x : qsort zs where
    (ys, zs) = partition (< x) xs

sieve :: (Num a) => [a] -> [a]
sieve (p:xs) = p : sieve [x | x <− xs, x ‘mod‘ p > 0]

primes = sieve [2..]
\Theta (n\ log\ log\ n) \leq \Theta (n^2\ / (log\ n)^2)
Θ(n log log n)Θ(n2 /(log n)2)\Theta (n\ log\ log\ n) \leq \Theta (n^2\ / (log\ n)^2)

Pros

Why should we even care?!

  • Control over State
  • Control over Side Effects
  • Purity
  • Modularity
  • Reasoning
  • Testing
  • Immutable structures
  • Thread-safe functions
  • Lazy with short circuits
  • Data safety
  • Concurrency
  • Parallelism
  • Useful abstractions:
    • Functor,
    • Monad,
    • Applicative...

Any questions?

Made with Slides.com