Skip to content

Instantly share code, notes, and snippets.

@mattclaw
Last active September 22, 2016 14:48
Show Gist options
  • Save mattclaw/554cf037cdf5d05425d738863e8cfe97 to your computer and use it in GitHub Desktop.
Save mattclaw/554cf037cdf5d05425d738863e8cfe97 to your computer and use it in GitHub Desktop.
Functional Programming & Async Notes

Front-end Masters FP & Async Workshop

0. INTRO

Most of developer time is spent READING and modifying existing code.

Average lines of code written per day: 5

Instead of spending time optimizing for writing code, optimize for readability.

Goal: Have confidence in your code changes.

Functional Programming

1. SIDE EFFECTS

Direct cause - values passed into function //Clarify

Side cause - values not passed into function but used by a function

Direct effect - value returned from function

Side effect - values changed from within a function, but not returned.

Mental overhead of side effects-programming is that you need to mentally execute many lines of code in order to fully understand a program.

Side effects not entirely avoidable; solution is to move side-effects to outer shell of the program. By avoiding side effects in your own code, you can more easily reason about it and have more confidence in its correctness.

2. PURE FUNCTIONS

A pure function has no side causes and no side effects. 'Referential transparency' - a fn whose output can be computed and cached without altering a program's behavior. Given the same inputs, always returns the same output.

If refactoring an impure function, extract the 'pure part' of the function and wrap it impure function. Alternatively, create an interface function.

3. COMPOSITION

DRY - Not about laziness, but about having confidence when modifying code that you only don't need to make the same logical change in more than one place.

Abstraction - Not necessarily about 'hiding' logic from someone. Perhaps better to consider abstraction as a way of letting you focus on what a function does or how to use that function, but not both at the same time. Less mental overhead.

Composition is an approach to take a solution of a problem and chunk it into simple functions and identify patterns of function calls.

A study has shown that code is more readable when it is familiar. Regardless of syntax or language, recognizable code is easier to reason about.

4. VALUE IMMUTABILITY

Constant - a variable which cannot be re-assigned. This is not the same as a value which never changes.

By treating your values as if they were immutable (whether they are or not) and by writing pure functions, you can reduce the frequency of bugs in your code.

5. CLOSURE

Closure is when a function "remembers" the variables around it even when that function is executed elsewhere.

Closure is consistent with functional programming if and only if every variable you close over is constant or treated as such.

Uses of closure

Partial application:

function partial(fn, ...args){
  return function partially(...moreArgs){
    return fn(...args, ...moreArgs);
  };
}

function add(x,y,z) {
  return x+y+z;
}
var add10 = partial(add, 10);

Currying:

function add(x,y,z) {
  return x+y+z;
}

var f = curry(add);

f(1)(2)(3); //6
```

### Eager vs Lazy Execution

* Eager: do work now
* Lazy: do work later

*Memoization* - trade confidence/purity for performance

```
function foo(x, y) {
  var sum;
  return function() {
    if (sum === undefined) {
      sum = x + y;
    }
    return sum;
  }
}

var x = foo(3,4);

x();  //7
x();  //7

```

## 6. RECURSION

Focus on the **what** of a problem instead of the **how**.  Leads to more
declarative code.

For instance, an imperative approach to finding a maximum number of a list would
be to iterate over the list and store the highest number seen.  But this is
focused too much on implementation details.

A recursive approach thinks about the problem differently.  Given a list of
numbers, the max number could be considered the maximum of the first element
in the list and the max number of the rest of the list.

```
function max(...nums) {
  if (nums.length == 1) return nums[0];
  return Math.max(nums[0], max(nums.slice(1)));
}
```

Even better,

```
function max(maxNum, ...nums){
  if(nums.length == 0) return maxNum;
  return Math.max(maxNum, max(...nums));
}
```

### Tail Call Optimization

Re-use stack frames in order to minimize memory usage.

```
function mult(product, num1, ...nums) {
	if( nums.length == 0 ){
		return product * num1;
	} else {
		return product * mult(num1, ...nums);
	}
}
```

becomes...

```
function mult(result, product, num1, ...nums) {
  result *= product * num1;
	if ( nums.length == 0 ) return result;
  return mult(result, ...nums);
}
```

But that's weird because the signature is strange; it relies on a reserved
function parameter to accumulate the result.  See slides for examples of
refactoring to proper tail-call form which maintains readable function
 signatures.

Hint: look for everything that you're saving for one stack from to the next and
turn those into the arguments for your outer function. //Clarify

## 7. LISTS

### Map

A transformation of a list of size *n* to a new list of size *n*.

Potential uses:
 * Transform a list of strings
 * Memoize a list of functions
 * Compose a list of functions with a given function
 * and more...

### Filter

A transformation of a list of size *n* to a new list of size * ≤ n*.

A filter implementation takes a *predicate function* which determines if a value
should be included or excluded from the result.

### Reduce

A transformation of a list which typically results in a single, discrete value
but can return any arbitrary new value such as an object or list (potentially
even longer than the input).

### Fusion
Reduce can be used to compose functions.  This is useful when you are mapping
over a list and performing multiple operations on each element in the list:

```
list = [1,2,3];

list.map(
  [div3,mul2,add1].reduce(combineRight)
);
```

...instead of

```
list = [1,2,3];

list
.map(add1)
.map(mul2)
.map(div3);
```

### Transduce

Often, we encounter a combination of map, filter, and reduce.  By using
transduction, we can eliminate superfluous iterations over the list.

# Asynchrony

## 8. What is Asynchrony?

### Async vs Parallel

Both asynchrony and parallelism are both types of concurrency, but function in
different ways:

* Parallelism
 - many units of computation all doing work at the same
time.  More performant for CPU-bound work.

* Asynchrony
 - one unit of computation performs work for multiple tasks over a period of
time, switching whenever a task blocks.  More performant for I/O bound work.

Asynchrony is the concurrency model used in the JavaScript ecosystem, and is
implemented with an event loop.

### Callbacks
A callback marks a location to resume execution after a function hands-off
execution.

Callback hell - is it simply just the pyramid of doom which causes pain?

#### Inversion of Control and callbacks

You have to trust your callback will be called:

1. not too early

2. not too late

3. not too many times

4. not too few times

5. no lost context

6. no swallowed errors

#### Callbacks Are Not Reasonable

Callbacks have no way to express temporal behavior in a synchronous way.  We
can't express flow control with callbacks in the way our brains think.

### Thunks

* Synchronous thunk is a wrapper around a value.
* An asynchronous thunk is a time-independent wrapper around a future value.
 - They can be used to more easily reason about state over time in your code. This
is the building block of promises.

### Promises

Asynchronous thunk with a fancier API.  The Promise API intends to solve trust
issues thanks to its design; can be thought of as a callback manager. Un-inverts
 inversion of control with event listeners.

1. Can only be resolved once
2. either success OR error
3. messages passed/kept
4. exceptions become errors
5. Immutable once resolved

FUTURE CHEESEBURGERS!!!!

```
_,__,_,,__
/ ; : ., : \
============
(((((())))))
\,_,,_,__,,/
```

#### Flow Control with Promises

Browsers support the following Promise strategies:

* Sequences - perform a series of steps
* Gate - all must resolve
* Race - first to resolve

## 9. GENERATORS

Allow a function to yield its execution.  Cooperative rather than pre-emptive.

Promises solve for trustability of code, un-inversion of control, while
generators solve for the callback hell problem.

```
yield   → promise
↑               ↓
.next() ← result
```

q.spawn, gen runner handle boilerplate of setting up promises and generators.
YDKJS Async & Performance Ch. 4 has an implementation of this, with comments.

## 10. OBSERVABLES

Event stream, lazy dual of an array.  Unfixed, infinitely extendable.

Observable, like an array.  With an observable, you can define a set of
operations to perform lazily as data streams in.

Zip / All -
Merge / Any -

## 10. CSP: COMMUNICATING SEQUENTIAL PROCESSES

Channel-based concurrency

A channel is a stream/pipe, but it can only have one message transferred at a
time (buffer size of one).

Channel messages are locally blocking.

"Processes" are concerns within your application which coordinate their
concurrency through blocking channel messages.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment