Functional Programming

is awesome!

A KSong Production

What is functional programming?

"Functional programming is programming without state..."

"Functional programming is a little bit like Microsoft Excel..." [paraphrased]

"Functional programming is Jesus Christ incarnate in a computer language..." [paraphrased]

"Functional Programming is when functions, not objects or procedures, are used as the fundamental building blocks of a program."

"Functional programming is the mustachioed hipster of programming paradigms...."

Functional programming is a style of programming which models computations as the evaluation of expressions.

"In computer science, functional programming is a programming paradigm..."

From Wikipedia

What is a programming paradigm?

What is a program?

"A computer program is a list of instructions that tell a computer what to do."

-Simple English Wikipedia

Objection 1

a = range(10)
for i in a:
    a[i] += i

..is just an abstraction for:

/* Return the access method type of the DBObject */
static int _DB_get_type(DBObject* self)
{
    DBTYPE type;
    int err;
    err = self->db->get_type(self->db, &type);
    if (makeDBError(err)) {
    return -1;
    }
    return type;
}
/* Create a DBT structure (containing key and data values) from Python
strings. Returns 1 on success, 0 on an error. */
static int make_dbt(PyObject* obj, DBT* dbt)
{
CLEAR_DBT(*dbt);
    if (obj == Py_None) {
        /* no need to do anything, the structure has already been zeroed */
    }
    else if (!PyArg_Parse(obj, "s#", &dbt->data, &dbt->size)) {
        PyErr_SetString(PyExc_TypeError,
        #if (PY_VERSION_HEX < 0x03000000)
        "Data values must be of type string or None.");
        #else
        "Data values must be of type bytes or None.");
        #endif
        return 0;
    }
    return 1;
}

..which is just an abstraction for:

...which abstracts:

...which in turn abstracts:

That, in turn, is really just:

...which in turn is made of:

...which rely on:

But that's just a lot of:

...which really approximates:

It really is turtles all the way down!

If we're going to program with abstractions anyways, why don't we choose the most powerful ones we can?

SELECT *
FROM Derps OUTER JOIN Herps
WHERE derp = false 
      AND herp = true
      AND derpherp < 50
GROUP BY merps,perps
ORDER BY burps
LIMIT 15;

Where are the instructions?

Objection 2

A program takes data and does things to it

The way we think about the program defines the paradigm

Programming Paradigms

Imperative

int listSum(int* input){
    int j, sum = 0;
    int listsize = GETARRSIZE(input); //Magic!
    for(j = 0; j < listsize; j++){
        sum += input[j];
    }
    return sum;
}

Things to note:

  • We have stateful variables: the values of sum and j change over the program.
  • We use statements to alter the state of the program
  • We think of the program as a machine that returns a final value.
  • As programmers, we are explicitly controlling the state of the program.

Imperative

Object-Oriented

int listSum(int* input){
    SumObject myadder = SumObject(input);
    myadder.calcSum();
    return myadder.getSum();
}

Huh? That's cheating!

Well...maybe.

How is SumObject implemented?

1 + 2 == 1.+(2)

We don't care!!

The important thing is not how the object is implemented.

Functional

int listSum(int* input){
    if(GETARRSIZE(input) == 0){
        return 0;
    }
    else return input[0] + listSum(input++);
}

 

  • A recursive definition

What else?

Functional (for realies)

--Haskell is sexy!

listSum :: [Int] -> Int

listSum [] = 0
listSum (x:xs) = x + listSum xs
int listSum(int* input){
    if(GETARRSIZE(input) == 0){
        return 0;
    }
    else return input[0] + listSum(input++);
}

Statements vs. Expressions

A statement alters the state of something

An expression has a value

Code can be both!

  • return 5;
  • log(10);
  • x + 5;
  • x + y;
  • some_function(x,y,z);
  • z = x + 5;

Functional programming is a style of programming which models computations as the evaluation of expressions.

Examples of functional thinking!

Find the last element of a list

last :: [a] -> a

last [] = error
last [x] = x
last (x:xs) = last xs
last ["a","b","c"]        c

 

Find the length of a list

listLen :: [a] -> Int

listLen [] = 0
listLen (x:xs) = 1 + listLen xs
length [a,a,a,a,a]       5

Find the k-th element of a list

element [1,1,2,3,5,8] 4       5
--Find the kth element of a list

element :: [a] -> Int -> a

element [] _ = error
element (x:xs) 0 = x
element (x:xs) k = element xs (k-1)

What are the features of functional languages?

Type Inferencing

Prelude> let f x = x + 1
Prelude> :type f
f :: Num a => a -> a

The compiler can infer the type of the function on its own!

 

Prevents you from cluttering your code by spelling out the explicit types of everything.

First-Class Functions

Prelude> let dotwice f = f f

Prelude> let twotimes x = 2*x

Prelude> let h = dotwice twotimes

Prelude> h 10
    40
Prelude> h 3
    12
Prelude> h 6
    24

Immutable Structures

What happened?

--test.hs
--Attempt to update value of x

x = 5
x = 3

Haskell does not allow for mutable values or data updates. Once you have declared a value, that value is not allowed to change!

Illegal operations:

x++

x += 5

Any sort of loop

Passing structures by reference

(Purity)

Wait...what???

Why use functional languages?

Powerful Abstractions

for(int i = 0; i < a.size(); i++){
    a[i] += 5;
}
for(int i = 0; i < a.size(); i++){
    a[i] *= 2;
}
for(int i = 0; i < a.size(); i++){
    if(a[i] %2 != 0){
        a[i] = -1; //Delete value
    }
}
int prod = 1;
for(int i = 0; i < a.size(); i++){
    prod *= a[i];
}
map (+5) a
map (*2) a
filter even a
foldl' (*) 1 a

Short(er) Code

segLS :: [Points] -> Double -> (Double,Int)
segLS [] cost = (0,1)
segLS [_] cost = (0,1)
segLS plist cost = minimumBy (compare `on` fst)  
                        [(costAtX x,x) | x <- [0..length plist - 1]]
                  where costAtX x = (fst $ segLS (take x plist) cost) 
                                  + slsErr (drop (x) plist) + cost

Segmented Least Squares -- Haskell

double sls_topDown_memoized(double* x, double* y, double** err, int* splitpts, int maxIndex){
  if(maxIndex == 0) return 0; //Offset cost for not splitting

  //Costs of not splitting;
  splitpts[maxIndex] = 0;
  if(err[0][maxIndex] == -1){
    err[0][maxIndex] = errors(0,maxIndex,x,y);
  }
  double minCost = err[0][maxIndex];

  for(int i = 0; i < maxIndex; i++){ //Consider costs of splitting
    if(err[i+1][maxIndex] == -1) err[i+1][maxIndex] = errors(i+1,maxIndex,x,y);

    double costHere = sls_topDown_naive(x,y,err,splitpts,i) + err[i+1][maxIndex] + costToSplit;
    if(costHere < minCost){
      minCost = costHere;
      splitpts[maxIndex] = i;
    }
  }

  return minCost;
}

Segmented Least Squares -- C++

Referential Transparency

What's wrong with not good about this code?

static int derp = 0;

int dumbAdd(int& a, int& b){
    if(derp == 0) derp = 1;
    else derp = 0; //switch derp 

    if(derp == 0) return 0;
    else return a++ + b++;
}

int a = 2, b = 3;
printf("%d\n", dumbAdd(a,b));

What does the code on the left return?

int mystery(){
    derp = 0;
    int a = 3, b = 2;
    a = dumbAdd(a,b);
    b = dumbAdd(a,b);
    a = dumbAdd(a,b);
    derp = 1;
    b = dumbAdd(a,b);
    return dumbAdd(a,b);
}
static int derp = 0;

int dumbAdd(int& a, int& b){
    if(derp == 0) derp = 1;
    else derp = 0;

    if(derp == 0) return 0;
    else return a++ + b++;
}

"Why do you do this to yourself?" --Kelvin Lwin

Referential Transparency: A function's output can only depend on its inputs and its definitions.

Lazy Execution :D

--isPrime is implemented elsewhere

allNums = [1..]
allPrimes = filter isPrime allNums
first1000 = take 1000 allPrimes

Find the first 1000 prime numbers

Expressiveness

void qsort(int a[], int lo, int hi) 
{
  int h, l, p, t;

  if (lo < hi) {
    l = lo;
    h = hi;
    p = a[hi];

    do {
      while ((l < h) && (a[l] <= p)) 
          l = l+1;
      while ((h > l) && (a[h] >= p))
          h = h-1;
      if (l < h) {
          t = a[l];
          a[l] = a[h];
          a[h] = t;
      }
    } while (l < h);

    a[hi] = a[l];
    a[l] = p;

    qsort( a, lo, l-1 );
    qsort( a, l+1, hi );
  }
}
quicksort :: Ord a => [a] -> [a]
quicksort []     = []
quicksort (p:xs) = (quicksort lesser) 
                   ++ [p] ++ 
                   (quicksort greater)
    where
        lesser  = filter (< p) xs
        greater = filter (>= p) xs

Expressiveness

--Looks for a value in a binary search tree.

Data Tree a = EmptyTree | Node Tree a Tree

target `in` EmptyTree = False
target `in` (Tree leftchild value rightchild)
    | target == value = True
    | target < value = target `in` leftchild
    | target > value = target `in` rightchild

It will make you a better programmer.

Downsides of functional languages

Some data types really are state-based!

  • Queues

  • Hash Tables

  • Random-Access Array

Hard to reason about memory

foldl (+) 0 [1..100000] --Stack overflow!

Hard to learn

Speed

Though it's actually not that bad...

Which functional language should I learn?

Options:

  • Haskell
  • Scala
  • Erlang
  • OCaml
  • Clojure
  • Lisps
    • Racket
    • Scheme
    • CommonLisp
  • ML-family
    • Standard ML
  • Javascript
  • And even more!

Haskell

  • One of the first standard purely-functional, lazily-executed languages
  • A "true" functional programming language: side-effecting only permitted in IO Monad
  • Notoriously heavily math-based:
    "A monad is a monoid in the category of endofunctors, what's the problem?"
  • Libraries slightly lacking
Difficulty: * * * * *

Time:       * * * * *

Purity:     * * * * *

Practical:  * *

ML/OCaml

Difficulty: * * *

Time:       * * *

Purity:     * * *  

Practical:  * * * *
  • Very popular pedagogical languages (aside from Lisps)
  • Very good libraries and optimizing compilers
  • Allow you to mutate values as an "escape hatch"
  • In some cases, faster than corresponding C/C++ program!

Scala

Difficulty: * * *

Time:       * * *

Purity:     *

Practical:  * * * * *
  • Written as a superset of Java. While the languages are not entirely compatible, much of it is.
  • Allows declarations of variables as val (immutable) and var (like in Java)
  • Runs on the JVM, full binary compatibility with Java libraries.
  • Already gaining a following in spite of being barely a decade old.
  • Seamlessly fuses OOP with FP

Lisps ("Lithpth")

Difficulty: * * *

Time:       * * * *

Purity:     * *

Practical:  * * *
  • Very popular language (Scheme Lisp used to be the language of choice for CS61B at Cal).
  • Stands for "List Processor". Everything is a list that gets evaluated.
  • One of the oldest and most successful functional languages, still sees heavy use in AI.
  • Members include Common Lisp, Scheme, Racket, and Clojure (a JVM-based Lisp)

JavaScript

Difficulty: ?

Time:       ?

Purity:     ?

Practical:  ?
  • Various libraries for functional: bacon, underscore, ramda
  • Matt was taught FP through ramda in under a week, so I'm assuming it's not horrible if you already understand JS.
  • JS was supposed to be functional---rumor has it that they were told to make the syntax "more like C" which is partially why it is the mess that it is.
  • Offers most of the functionality of FP with full access to the rest of JS, e.g. Node

This is just scratching the surface of FP!

“But I don’t want to go among mad people," Alice remarked.


"Oh, you can’t help that," said the Cat: "we’re all mad here. I’m mad. You’re mad."


"How do you know I’m mad?" said Alice.


"You must be," said the Cat, "or you wouldn’t have come here.”

FIN