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

Functional Programming

By Kevin Song

Functional Programming

Why is FP so popular these days?

  • 966