Skip to content

Instantly share code, notes, and snippets.

@miguel-vila
Last active January 4, 2016 12:29
Show Gist options
  • Save miguel-vila/ebba612e720d48adf321 to your computer and use it in GitHub Desktop.
Save miguel-vila/ebba612e720d48adf321 to your computer and use it in GitHub Desktop.

#Chapter 1

  • Side effects, by definition, are not tracked in the types, and so the compiler cannot alert us if we forget to perform some action as a side effect.
  • In general, we'll learn how this sort of transformation can be applied to any function with side effects to push these effects to the "outer layers" of the program. Functional programmers often speak of implementing programs with a pure core and a thin layer on the outside that handles effects.
  • (...) a function has no observable effect on the execution of the program other than to compute a result given its inputs; we say that it has no side effects.
  • We can formalize this idea of pure functions using the concept of referential transparency (RT). This is a property of expressions in general and not just functions.
  • This is all it means for an expression to be referentially transparent—in any program, the expression can be replaced by its result without changing the meaning of the program.
  • This definition should give you some intuition that referential transparency forces all effects to be returned as values, in the result type of the function.
  • RT enables equational reasoning about programs.
  • Conversely, the substitution model is simple to reason about since effects of evaluation are purely local (they affect only the expression being evaluated) and we need not mentally simulate sequences of state updates to understand a block of code. Understanding requires only local reasoning.
  • A modular program consists of components that can be understood and reused independently of the whole (...)
  • A pure function is modular and composable because it separates the logic of the computation itself from "what to do with the result" and "how to obtain the input"; it is a black box.

#Chapter 2

  • (...) the universe of possible implementations is significantly reduced when implementing a polymorphic function.
  • If a function is polymorphic in some type A, the only operations that can be performed on that A are those passed into the function as arguments (or that can be defined in terms of these given operations)
  • In some cases, you'll find that the universe of possibilities for a given polymorphic type is constrained such that there exists only a single implementation!
  • La mejor expicación de currying:
def compose[A,B,C](f: B => C, g: A => B): A => C

If you can exchange a banana for a carrot and an apple for a banana, you can exchange an apple for a carrot.

  • Polymorphic, higher-order functions often end up being extremely widely applicable, precisely because they say nothing about any particular domain and are simply abstracting over a common pattern that occurs in many contexts. For this reason, programming in the large has very much the same flavor as programming in the small.

#Chapter 4

  • Another way of understanding RT is that the meaning of RT expressions does not depend on context and may be reasoned about locally, while the meaning of non-RT expressions is context dependent and requires more global reasoning.
  • The problems with exceptions:

..* (...) exceptions break RT and introduce context dependence. (...) This is the source of the folklore advice that exceptions should be used only for "error handling", not for "control flow".

..* Exceptions are not typesafe. The type of failingFn is Int => Int, which tells us nothing about the fact that exceptions may occur, and the compiler will certainly not force callers of failingFn to make a decision about whether to reraise or handle any exceptions thrown by failingFn. If we accidentally forget to check for an exception in failingFn, this won't be detected until runtime.

#Chapter 5

  • To say a function is non-strict just means that the function may choose not to evaluate one or more of its arguments. In contrast, a strict function always evaluates its arguments.
  • Formal definition of strictness:

If the evaluation of an expression runs forever or throws an error instead of returning a definite value, we say that the expression does not terminate, or that it evaluates to bottom. A function f is strict if the expression f(x) evaluates to bottom for all x that evaluate to bottom.

  • More generally speaking, laziness lets us separate the description of an expression from the evaluation of that expression. This gives us a powerful ability — we may choose to describe a "larger" expression than we need, then evaluate only a portion of it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment