diff --git a/lectures/progfun1-1-1.md b/lectures/progfun1-1-1.md
new file mode 100644
index 0000000000000000000000000000000000000000..bc05bfd966aaae39ddc89353357f8ac39faec015
--- /dev/null
+++ b/lectures/progfun1-1-1.md
@@ -0,0 +1,204 @@
+% Functional Programming Principles in Scala
+% Martin Odersky
+% \today
+
+Programming Paradigms
+=====================
+
+Paradigm: In science, a \red{paradigm} describes distinct concepts or
+thought patterns in some scientific discipline.
+
+Main programming paradigms:
+
+ - imperative programming
+ - functional programming
+ - logic programming
+
+Orthogonal to it:
+
+ - object-oriented programming
+
+Review: Imperative programming
+==============================
+
+Imperative programming is about
+
+ - modifying mutable variables,
+ - using assignments
+ - and control structures such as if-then-else, loops, break, continue, return.
+
+The most common informal way to understand imperative programs is as instruction sequences for a Von Neumann computer.
+
+Imperative Programs and Computers
+=================================
+
+There's a strong correspondence between
+
+\begin{tabular}{lll}
+Mutable variables & $\textcolor{navy}{\approx}$ & memory cells \\
+Variable dereferences & $\textcolor{navy}{\approx}$ & load instructions \\
+Variable assignments & $\textcolor{navy}{\approx}$ & store instructions \\
+Control structures & $\textcolor{navy}{\approx}$ & jumps
+\end{tabular}
+\bigskip
+
+\red{Problem}: Scaling up. How can we avoid conceptualizing programs word by word?
+
+\red{Reference}: John Backus, Can Programming Be Liberated from the von. Neumann Style?, Turing Award Lecture 1978.
+
+Scaling Up
+==========
+
+In the end, pure imperative programming is limited by the "Von Neumann"
+bottleneck:
+
+\begin{quote}
+One tends to conceptualize data structures word-by-word.
+\end{quote}
+
+We need other techniques for defining high-level abstractions such as collections, polynomials, geometric shapes, strings, documents.
+
+Ideally: Develop _theories_ of collections, shapes, strings, ...
+
+What is a Theory?
+=================
+
+A theory consists of
+
+ - one or more _data types_
+ - _operations_ on these types
+ - _laws_ that describe the relationships between values and operations
+
+Normally, a theory does not describe mutations!
+
+Theories without Mutation
+=========================
+
+For instance the theory of polynomials defines the sum of two polynomials by laws such as:
+
+      (a*x + b) + (c*x + d) = (a + c)*x + (b + d)
+
+But it does not define an operator to change a coefficient while keeping the polynomial the same!
+->
+Whereas in an imperative program one _can_ write:
+
+\begin{lstlisting}
+      class Polynomial { double[] coefficient; }
+      Polynomial p = ...;
+      p.coefficient[0] = 42;
+\end{lstlisting}
+
+Theories without Mutation
+=========================
+
+\red{Other example:}
+
+The theory of strings defines a concatenation operator ++ which is associative:
+
+      (a ++ b) ++ c  =  a ++ (b ++ c)
+
+But it does not define an operator to change a sequence element while keeping the sequence the same!
+
+(This one, some languages _do_ get right; e.g. Java's strings are immutable)
+
+Consequences for Programming
+============================
+
+If we want to implement high-level concepts following their mathematical theories,
+there's no place for mutation.
+
+ - The theories do not admit it.
+ - Mutation can destroy useful laws in the theories.
+
+Therefore, let's
+
+ - concentrate on defining theories for operators expressed as functions,
+ - avoid mutations,
+ - have powerful ways to abstract and compose functions.
+
+Functional Programming
+======================
+
+ - In a _restricted_ sense, functional programming (FP) means programming without mutable variables, assignments, loops, and other imperative control structures.
+ - In a _wider_ sense, functional programming means focusing on the functions and immutable data.
+ - In particular, functions can be values that are produced, consumed, and composed.
+ - All this becomes easier in a functional language.
+
+Functional Programming Languages
+================================
+
+ - In a _restricted_ sense, a functional programming language is one which does not have mutable variables, assignments, or imperative control structures.
+ - In a _wider_ sense, a functional programming language enables the
+  construction of elegant programs that focus on functions and immutable data structures.
+ - In particular, functions in a FP language are first-class citizens. This means
+   \begin{itemize}
+   \item they can be defined anywhere, including inside other functions
+   \item like any other value, they can be passed as parameters to functions and returned as results
+   \item as for other values, there exists a set of operators to compose functions
+   \end{itemize}
+
+Some functional programming languages
+=====================================
+
+In the restricted sense:
+
+ - Pure Lisp, XSLT, XPath, XQuery, FP
+ - Haskell (without I/O Monad or UnsafePerformIO)
+
+In the wider sense:
+
+ - (Lisp, Scheme), Racket, Clojure
+ - SML, Ocaml, F\#
+ - Haskell (full language)
+ - Scala
+ - (Smalltalk, Ruby)
+
+(...): languages with first class functions but incomplete support for immutable data
+
+History of FP languages
+=======================
+
+\begin{tabular}{llp{2cm}ll}
+    1959 & (Lisp)             && 2003 & Scala
+\\  1975-77 & ML, FP, Scheme  && 2005 & F\#
+\\  1978 & (Smalltalk)        && 2007 & Clojure
+\\  1986 & Standard ML        && 2012 & Elixir
+\\  1990 & Haskell, Erlang    && 2014 & Swift
+\\  2000 & OCaml              && 2017 & Idris
+\\       &                    && 2020 & Scala 3
+\end{tabular}
+
+Scala 3 is the language we will use in this course.
+
+Recommended Book (1)
+====================
+
+Structure and Interpretation of Computer Programs. Harold
+Abelson and Gerald J. Sussman. 2nd edition. MIT Press 1996.
+
+\includegraphics[scale=0.4]{images/sicp.jpg}
+
+A classic. Many parts of the course and quizzes are based on it,
+but we change the language from Scheme to Scala.
+
+The full text [\blue{can be downloaded here.}](http://mitpress.mit.edu/sicp/)
+
+
+Recommended Book (2)
+====================
+
+Programming in Scala. Martin Odersky, Lex Spoon, and Bill Venners. 3rd edition. Artima 2016.
+
+\includegraphics[scale=0.2]{images/cover.pdf}
+
+The standard language introduction and reference.
+
+Other Recommended Books
+=======================
+
+\ \hfill
+\includegraphics[scale=0.25]{images/scala-impatient.png} \hfill
+\includegraphics[scale=0.33]{images/programming-scala.jpeg} \hfill
+\includegraphics[scale=0.45]{images/scala-in-depth.jpeg} \hfill\
+
+
diff --git a/lectures/progfun1-1-2.md b/lectures/progfun1-1-2.md
new file mode 100644
index 0000000000000000000000000000000000000000..65ab2da4286ac07690bc21438a9f149ef440d4d7
--- /dev/null
+++ b/lectures/progfun1-1-2.md
@@ -0,0 +1,437 @@
+% Elements of Programming
+%
+%
+
+Elements of Programming
+=======================
+
+Every non-trivial programming language provides:
+
+ - primitive expressions representing the simplest elements
+ - ways to _combine_ expressions
+ - ways to _abstract_ expressions, which introduce a name for an expression by which it can then be referred to.
+
+The Read-Eval-Print Loop
+========================
+
+Functional programming is a bit like using a calculator
+
+An interactive shell (or REPL, for Read-Eval-Print-Loop) lets one
+write expressions and responds with their value.
+
+The Scala REPL can be started by simply typing
+
+\begin{lstlisting}
+> scala
+\end{lstlisting}
+
+Expressions
+===========
+
+Here are some simple interactions with the REPL
+
+    scala> 87 + 145
+    res0: Int = 232
+
+Functional programming languages are more than simple calcululators
+because they let one define values and functions:
+
+    scala> def size = 2
+    size: Int
+
+    scala> 5 * size
+    res1: Int = 10
+
+Evaluation
+==========
+
+A non-primitive expression is evaluated as follows.
+
+  1. Take the leftmost operator
+  2. Evaluate its operands (left before right)
+  3. Apply the operator to the operands
+
+A name is evaluated by replacing it with the right hand side of its definition
+
+The evaluation process stops once it results in a value
+
+A value is a number (for the moment)
+
+Later on we will consider also other kinds of values
+
+Example
+=======
+
+Here is the evaluation of an arithmetic expression:
+
+    def pi = 3.14159
+
+    def radius = 10
+
+    (2 * pi) * radius
+
+Example
+=======
+
+Here is the evaluation of an arithmetic expression:
+
+    (2 * pi) * radius
+
+    (2 * 3.14159) * radius
+
+Example
+=======
+
+Here is the evaluation of an arithmetic expression:
+
+    (2 * pi) * radius
+
+    (2 * 3.14159) * radius
+
+    6.28318 * radius
+
+Example
+=======
+
+Here is the evaluation of an arithmetic expression:
+
+    (2 * pi) * radius
+
+    (2 * 3.14159) * radius
+
+    6.28318 * radius
+
+    6.28318 * 10
+
+Example
+=======
+
+Here is the evaluation of an arithmetic expression:
+
+    (2 * pi) * radius
+
+    (2 * 3.14159) * radius
+
+    6.28318 * radius
+
+    6.28318 * 10
+
+    62.8318
+
+Parameters
+==========
+
+Definitions can have parameters. For instance:
+
+    scala> def square(x: Double) = x * x
+    square: (x: Double)Double
+
+    scala> square(2)
+    4.0
+
+    scala> square(5 + 4)
+    81.0
+
+    scala> square(square(4))
+    256.0
+
+    scala> def sumOfSquares(x: Double, y: Double) = square(x) + square(y)
+    sumOfSquares: (x: Double, y: Double)Double
+
+
+Parameter and Return Types
+==========================
+
+Function parameters come with their type, which is given after a colon
+
+      def power(x: Double, y: Int): Double = ...
+
+If a return type is given, it follows the parameter list.
+
+Primitive types are as in Java, but are written capitalized, e.g:
+
+\begin{tabular}{ll}
+   \verb@Int@       & 32-bit integers
+\\ \verb@Double@   & 64-bit floating point numbers
+\\ \verb@Boolean@   & boolean values \verb@true@ and \verb@false@
+\end{tabular}
+
+
+
+Evaluation of Function Applications
+===================================
+
+Applications of parameterized functions are evaluated in a similar way as operators:
+
+ 1. Evaluate all function arguments, from left to right
+ 2. Replace the function application by the function's right-hand side, and, at the same time
+ 3. Replace the formal parameters of the function by the actual arguments.
+
+Example
+=======
+
+    sumOfSquares(3, 2+2)
+
+
+Example
+=======
+
+    sumOfSquares(3, 2+2)
+    sumOfSquares(3, 4)
+
+
+Example
+=======
+
+    sumOfSquares(3, 2+2)
+    sumOfSquares(3, 4)
+    square(3) + square(4)
+
+
+Example
+=======
+
+    sumOfSquares(3, 2+2)
+    sumOfSquares(3, 4)
+    square(3) + square(4)
+    3 * 3 + square(4)
+
+
+
+Example
+=======
+
+    sumOfSquares(3, 2+2)
+    sumOfSquares(3, 4)
+    square(3) + square(4)
+    3 * 3 + square(4)
+    9 + square(4)
+
+
+Example
+=======
+
+    sumOfSquares(3, 2+2)
+    sumOfSquares(3, 4)
+    square(3) + square(4)
+    3 * 3 + square(4)
+    9 + square(4)
+    9 + 4 * 4
+
+Example
+=======
+
+    sumOfSquares(3, 2+2)
+    sumOfSquares(3, 4)
+    square(3) + square(4)
+    3 * 3 + square(4)
+    9 + square(4)
+    9 + 4 * 4
+    9 + 16
+
+
+Example
+=======
+
+    sumOfSquares(3, 2+2)
+    sumOfSquares(3, 4)
+    square(3) + square(4)
+    3 * 3 + square(4)
+    9 + square(4)
+    9 + 4 * 4
+    9 + 16
+    25
+
+
+
+The substitution model
+======================
+
+This scheme of expression evaluation is called the _substitution model_.
+
+The idea underlying this model is that all evaluation does is _reduce
+an expression to a value_.
+
+It can be applied to all expressions, as long as they have no side effects.
+
+The substitution model is formalized in the \red{$\lambda$-calculus}, which gives a
+foundation for functional programming.
+
+Termination
+===========
+
+> - Does every expression reduce to a value (in a finite number of steps)?
+
+
+Termination
+===========
+
+> - Does every expression reduce to a value (in a finite number of steps)?
+> - No. Here is a counter-example
+
+              def loop: Int = loop
+
+              loop
+
+Changing the evaluation strategy
+================================
+
+The interpreter reduces function arguments to values before rewriting the function application.
+
+One could alternatively apply the function to unreduced arguments.
+
+For instance:
+
+       sumOfSquares(3, 2+2)
+
+Changing the evaluation strategy
+================================
+
+The interpreter reduces function arguments to values before rewriting the function application.
+
+One could alternatively apply the function to unreduced arguments.
+
+For instance:
+
+       sumOfSquares(3, 2+2)
+       square(3) + square(2+2)
+
+Changing the evaluation strategy
+================================
+
+The interpreter reduces function arguments to values before rewriting the function application.
+
+One could alternatively apply the function to unreduced arguments.
+
+For instance:
+
+       sumOfSquares(3, 2+2)
+       square(3) + square(2+2)
+       3 * 3 + square(2+2)
+
+Changing the evaluation strategy
+================================
+
+The interpreter reduces function arguments to values before rewriting the function application.
+
+One could alternatively apply the function to unreduced arguments.
+
+For instance:
+
+       sumOfSquares(3, 2+2)
+       square(3) + square(2+2)
+       3 * 3 + square(2+2)
+       9 + square(2+2)
+
+Changing the evaluation strategy
+================================
+
+The interpreter reduces function arguments to values before rewriting the function application.
+
+One could alternatively apply the function to unreduced arguments.
+
+For instance:
+
+       sumOfSquares(3, 2+2)
+       square(3) + square(2+2)
+       3 * 3 + square(2+2)
+       9 + square(2+2)
+       9 + (2+2) * (2+2)
+
+Changing the evaluation strategy
+================================
+
+The interpreter reduces function arguments to values before rewriting the function application.
+
+One could alternatively apply the function to unreduced arguments.
+
+For instance:
+
+       sumOfSquares(3, 2+2)
+       square(3) + square(2+2)
+       3 * 3 + square(2+2)
+       9 + square(2+2)
+       9 + (2+2) * (2+2)
+       9 + 4 * (2+2)
+
+Changing the evaluation strategy
+================================
+
+The interpreter reduces function arguments to values before rewriting the function application.
+
+One could alternatively apply the function to unreduced arguments.
+
+For instance:
+
+       sumOfSquares(3, 2+2)
+       square(3) + square(2+2)
+       3 * 3 + square(2+2)
+       9 + square(2+2)
+       9 + (2+2) * (2+2)
+       9 + 4 * (2+2)
+       9 + 4 * 4
+
+Changing the evaluation strategy
+================================
+
+The interpreter reduces function arguments to values before rewriting the function application.
+
+One could alternatively apply the function to unreduced arguments.
+
+For instance:
+
+       sumOfSquares(3, 2+2)
+       square(3) + square(2+2)
+       3 * 3 + square(2+2)
+       9 + square(2+2)
+       9 + (2+2) * (2+2)
+       9 + 4 * (2+2)
+       9 + 4 * 4
+       25
+
+
+Call-by-name and call-by-value
+==============================
+
+The first evaluation strategy is known as _call-by-value_,
+the second is is known as _call-by-name_.
+
+Both strategies reduce to the same final values
+as long as
+
+ - the reduced expression consists of pure functions, and
+ - both evaluations terminate.
+
+Call-by-value has the advantage that it evaluates every function argument only once.
+
+Call-by-name has the advantage that a function argument is not evaluated if the corresponding parameter is unused in the evaluation of the function body.
+
+
+Call-by-name vs call-by-value
+=============================
+
+\quiz
+
+Question: Say you are given the following function definition:
+
+      def test(x: Int, y: Int) = x * x
+
+For each of the following function applications, indicate which evaluation strategy is fastest (has the fewest reduction steps)
+
+      CBV     CBN      same
+    fastest  fastest  #steps
+
+       O       O        O          test(2, 3)
+       O       O        O          test(3+4, 8)
+       O       O        O          test(7, 2*4)
+       O       O        O          test(3+4, 2*4)
+
+Call-by-name vs call-by-value
+=============================
+
+      def test(x: Int, y: Int) = x * x
+
+      test(2, 3)
+      test(3+4, 8)
+      test(7, 2*4)
+      test(3+4, 2*4)
diff --git a/lectures/progfun1-1-3.md b/lectures/progfun1-1-3.md
new file mode 100644
index 0000000000000000000000000000000000000000..c3251354a6d10186042723a1757d2e712393f469
--- /dev/null
+++ b/lectures/progfun1-1-3.md
@@ -0,0 +1,90 @@
+% Evaluation Strategies and Termination
+%
+%
+
+Call-by-name, Call-by-value and termination
+===========================================
+
+You know from the last module that the call-by-name and call-by-value
+evaluation strategies reduce an expression to the same value,
+as long as both evaluations terminate.
+
+But what if termination is not guaranteed?
+
+We have:
+
+ - If CBV evaluation of an expression $e$ terminates, then CBN evaluation of $e$ terminates, too.
+ - The other direction is not true
+
+Non-termination example
+=======================
+
+Question: Find an expression that terminates under CBN but not under CBV.
+
+\quiz
+
+Non-termination example
+=======================
+
+Let's define
+
+      def first(x: Int, y: Int) = x
+
+and consider the expression `first(1, loop)`.
+
+\bigskip
+\mbox{Under CBN: ~~~~~~~~~~~~~~~~~~~~~~~ Under CBV:}
+\bigskip
+
+      first(1, loop)                first(1, loop)
+
+Scala's evaluation strategy
+===========================
+
+Scala normally uses call-by-value.
+
+But if the type of a function parameter starts with `=>` it uses call-by-name.
+
+Example:
+
+      def constOne(x: Int, y: => Int) = 1
+
+Let's trace the evaluations of
+
+      constOne(1+2, loop)
+
+and
+
+      constOne(loop, 1+2)
+
+Trace of `constOne(1 + 2, loop)`
+================================
+
+        constOne(1 + 2, loop)
+
+Trace of `constOne(1 + 2, loop)`
+================================
+
+        constOne(1 + 2, loop)
+        constOne(3, loop)
+
+Trace of `constOne(1 + 2, loop)`
+================================
+
+        constOne(1 + 2, loop)
+        constOne(3, loop)
+        1
+
+Trace of `constOne(loop, 1 + 2)`
+================================
+
+        constOne(loop, 1 + 2)
+
+Trace of `constOne(loop, 1 + 2)`
+================================
+
+        constOne(loop, 1 + 2)
+        constOne(loop, 1 + 2)
+        constOne(loop, 1 + 2)
+        ...
+
diff --git a/lectures/progfun1-1-4.md b/lectures/progfun1-1-4.md
new file mode 100644
index 0000000000000000000000000000000000000000..a7f399b4fc02f16d986835e4e220609cd632fa93
--- /dev/null
+++ b/lectures/progfun1-1-4.md
@@ -0,0 +1,111 @@
+% Conditionals and Value Definitions
+%
+%
+
+Conditional Expressions
+=======================
+
+To express choosing between two alternatives, Scala
+has a conditional expression `if-then-else`.
+
+It resembles an `if-else` in Java, but is used for expressions, not statements.
+
+Example:
+
+      def abs(x: Int) = if x >= 0 then x else -x
+
+`x >= 0` is a _predicate_, of type `Boolean`.
+
+Boolean Expressions
+===================
+
+Boolean expressions `b` can be composed of
+
+      true  false      // Constants
+      !b               // Negation
+      b && b           // Conjunction
+      b || b           // Disjunction
+
+and of the usual comparison operations:
+
+      e <= e, e >= e, e < e, e > e, e == e, e != e
+
+Rewrite rules for Booleans
+==========================
+
+Here are reduction rules for Boolean expressions (`e` is an arbitrary expression):
+
+      !true       -->   false
+      !false      -->   true
+      true && e   -->   e
+      false && e  -->   false
+      true || e   -->   true
+      false || e  -->   e
+
+Note that `&&` and `||` do not always need their right operand to be evaluated.
+
+We say, these expressions use "short-circuit evaluation".
+
+Exercise: Formulate rewrite rules for if-then-else
+=================================================
+
+\quiz
+
+
+
+
+Value Definitions
+=================
+
+We have seen that function parameters can be passed by value or be passed by name.
+
+The same distinction applies to definitions.
+
+The `def` form is "by-name", its right hand side is evaluated on each use.
+
+There is also a `val` form, which is "by-value". Example:
+
+      val x = 2
+      val y = square(x)
+
+The right-hand side of a `val` definition is evaluated at the point of the definition itself.
+
+Afterwards, the name refers to the value.
+
+For instance, `y` above refers to `4`, not `square(2)`.
+
+Value Definitions and Termination
+=================================
+
+The difference between `val` and `def` becomes apparent when the right
+hand side does not terminate. Given
+
+      def loop: Boolean = loop
+
+A definition
+
+      def x = loop
+
+is OK, but a definition
+
+      val x = loop
+
+will lead to an infinite loop.
+
+
+Exercise
+========
+
+\quiz
+
+Write functions `and` and `or` such that for all argument expressions `x` and `y`:
+
+     and(x, y)    ==    x && y
+     or(x, y)     ==    x || y
+
+(do not use `||` and `&&` in your implementation)
+
+What are good operands to test that the equalities hold?
+
+
+
diff --git a/lectures/progfun1-1-5.md b/lectures/progfun1-1-5.md
new file mode 100644
index 0000000000000000000000000000000000000000..5c6e814b8cece95efcbbb8faab92151afe519201
--- /dev/null
+++ b/lectures/progfun1-1-5.md
@@ -0,0 +1,84 @@
+% Example: Square roots with Newton's method
+%
+%
+Task
+====
+
+We will define in this session a function
+
+      /** Calculates the square root of parameter x */
+      def sqrt(x: Double): Double = ...
+
+The classical way to achieve this is by successive approximations using
+Newton's method.
+
+Method
+======
+
+To compute `sqrt(x)`:
+
+ - Start with an initial _estimate_ `y` (let's pick `y = 1`).
+ - Repeatedly improve the estimate by taking the mean of `y` and `x/y`.
+
+Example:
+
+    Estimation          Quotient              Mean
+
+    1                   2 / 1 = 2             1.5
+    1.5                 2 / 1.5 = 1.333       1.4167
+    1.4167              2 / 1.4167 = 1.4118   1.4142
+    1.4142              ...                   ...
+
+
+Implementation in Scala (1)
+===========================
+
+First, define a function which computes one iteration step
+
+      def sqrtIter(guess: Double, x: Double): Double =
+        if isGoodEnough(guess, x) then guess
+        else sqrtIter(improve(guess, x), x)
+
+Note that `sqrtIter` is _recursive_, its right-hand side calls itself.
+
+Recursive functions need an explicit return type in Scala.
+
+For non-recursive functions, the return type is optional
+
+Implementation in Scala (2)
+===========================
+
+Second, define a function `improve` to improve an estimate and a test to check for terminatation:
+
+      def improve(guess: Double, x: Double) =
+        (guess + x / guess) / 2
+
+      def isGoodEnough(guess: Double, x: Double) =
+        abs(guess * guess - x) < 0.001
+
+Implementation in Scala (3)
+===========================
+
+Third, define the `sqrt` function:
+
+      def sqrt(x: Double) = sqrtIter(1.0, x)
+
+Exercise
+========
+
+\quiz
+
+1. The `isGoodEnough` test is not very precise for small numbers and can
+lead to non-termination for very large numbers. Explain why.
+
+2. Design a different version of `isGoodEnough` that does not have these problems.
+
+3. Test your version with some very very small and large numbers, e.g. \par
+
+\begin{verbatim}
+        0.001
+        0.1e-20
+        1.0e20
+        1.0e50
+\end{verbatim}
+
diff --git a/lectures/progfun1-1-6.md b/lectures/progfun1-1-6.md
new file mode 100644
index 0000000000000000000000000000000000000000..c21d0f2850c8946a99a15c70a739dbb02f2db179
--- /dev/null
+++ b/lectures/progfun1-1-6.md
@@ -0,0 +1,195 @@
+% Blocks and Lexical Scope
+%
+%
+Nested functions
+================
+
+It's good functional programming style to split up a task into many small functions.
+
+But the names of functions like `sqrtIter`, `improve`, and `isGoodEnough` matter only for the _implementation_ of `sqrt`, not for its _usage_.
+
+Normally we would not like users to access these functions directly.
+
+We can achieve this and at the same time avoid "name-space pollution" by
+putting the auxciliary functions inside `sqrt`.
+
+The `sqrt` Function, Take 2
+===========================
+
+      def sqrt(x: Double) = {
+        def sqrtIter(guess: Double, x: Double): Double =
+          if isGoodEnough(guess, x) then guess
+          else sqrtIter(improve(guess, x), x)
+
+        def improve(guess: Double, x: Double) =
+          (guess + x / guess) / 2
+
+        def isGoodEnough(guess: Double, x: Double) =
+          abs(square(guess) - x) < 0.001
+
+        sqrtIter(1.0, x)
+      }
+
+The `sqrt` Function, Take 2
+===========================
+
+      def sqrt(x: Double) =
+        def sqrtIter(guess: Double, x: Double): Double =
+          if isGoodEnough(guess, x) then guess
+          else sqrtIter(improve(guess, x), x)
+
+        def improve(guess: Double, x: Double) =
+          (guess + x / guess) / 2
+
+        def isGoodEnough(guess: Double, x: Double) =
+          abs(square(guess) - x) < 0.001
+
+        sqrtIter(1.0, x)
+
+In Scala 3, braces are optional for indented code.
+
+Blocks in Scala
+===============
+
+- A block is delimited by braces `{ ... }`.
+\begin{verbatim}
+    { val x = f(3)
+      x * x
+    }
+\end{verbatim}
+- It contains a sequence of definitions or expressions.
+- The last element of a block is an expression that defines its value.
+- This return expression can be preceded by auxiliary definitions.
+- Blocks are themselves expressions; a block may appear everywhere an expression can.
+- In Scala 3, braces are optional (i.e. implied) around a correctly indented expression that appears after `=`, `then`, `else`, ... .
+
+Blocks and Visibility
+=====================
+
+                val x = 0
+                def f(y: Int) = y + 1
+                val result =
+                  val x = f(3)
+                  x * x
+
+- The definitions inside a block are only visible from within the block.
+- The definitions inside a block _shadow_ definitions of the same names
+  outside the block.
+
+Exercise: Scope Rules
+=====================
+
+\quiz
+
+Question: What is the value of `result` in the following program?
+
+                val x = 0
+                def f(y: Int) = y + 1
+                val result = {
+                  val x = f(3)
+                  x * x
+                } + x
+
+Possible answers:
+
+       O        0
+       O        16
+       O        32
+       O        reduction does not terminate
+
+
+
+Lexical Scoping
+===============
+
+Definitions of outer blocks are visible inside a block unless they are shadowed.
+
+Therefore, we can simplify `sqrt` by eliminating redundant occurrences of the `x` parameter, which means everywhere the same thing:
+
+The `sqrt` Function, Take 3
+===========================
+
+      def sqrt(x: Double) =
+        def sqrtIter(guess: Double): Double =
+          if isGoodEnough(guess) then guess
+          else sqrtIter(improve(guess))
+
+        def improve(guess: Double) =
+          (guess + x / guess) / 2
+
+        def isGoodEnough(guess: Double) =
+          abs(square(guess) - x) < 0.001
+
+        sqrtIter(1.0)
+
+End Markers
+===========
+
+With heavily indented code it is sometimes hard to see where a construct ends.
+
+End markers are a tool to make this explicit.
+
+        def f() =
+
+          ...
+            ...
+              ...
+
+        end f
+
+ - And end marker is followed by the name that's defined in the
+   definition that ends at this point.
+
+ - It must align with the opening keyword (`def` in this case).
+
+The `sqrt` Function, Take 4
+===========================
+
+      def sqrt(x: Double) =
+        def sqrtIter(guess: Double): Double =
+          if isGoodEnough(guess) then guess
+          else sqrtIter(improve(guess))
+
+        def improve(guess: Double) =
+          (guess + x / guess) / 2
+
+        def isGoodEnough(guess: Double) =
+          abs(square(guess) - x) < 0.001
+
+        sqrtIter(1.0)
+      end sqrt
+
+Semicolons
+==========
+
+In Scala, semicolons at the end of lines are in most cases optional
+
+You could write
+
+    val x = 1;
+
+but most people would omit the semicolon.
+
+On the other hand, if there are more than one statements on a line, they need to be separated by semicolons:
+
+    val y = x + 1; y * y
+
+Summary
+=======
+
+You have seen simple elements of functional programing in Scala.
+
+- arithmetic and boolean expressions
+- conditional expressions if-then-else
+- functions with recursion
+- nesting and lexical scope
+
+You have learned the difference between the call-by-name and
+call-by-value evaluation strategies.
+
+You have learned a way to reason about program execution: reduce expressions using the substitution model.
+
+This model will be an important tool for the coming sessions.
+
+
+
diff --git a/lectures/progfun1-1-7.md b/lectures/progfun1-1-7.md
new file mode 100644
index 0000000000000000000000000000000000000000..0476effd36c73077dd4f73b58c4fb8c026b43c6f
--- /dev/null
+++ b/lectures/progfun1-1-7.md
@@ -0,0 +1,135 @@
+% Tail Recursion
+%
+%
+
+Review: Evaluating a Function Application
+=========================================
+
+One simple rule : One evaluates a function application $\btt f(e_1, ..., e_n)$
+
+- by evaluating the expressions $\btt e_1, \ldots, e_n$ resulting in the values
+$\btt v_1, ..., v_n$, then
+- by replacing the application with the body of the function `f`,
+in which
+- the actual parameters $\btt v_1, ..., v_n$ replace the
+formal parameters of `f`.
+
+
+Application Rewriting Rule
+==========================
+
+This can be formalized as a \red{rewriting of the program itself}:
+
+$$
+\begin{array}{ll}
+  &\btt def\ f (x_1, ..., x_n) = B ;\ ...\ f (v_1, ..., v_n)    \\
+ \rightarrow & \\
+  &\btt def\ f (x_1, ..., x_n) = B ;\ ...\ [v_1/x_1, ..., v_n/x_n]\,B
+\end{array}
+$$
+
+
+Here, $\btt [v_1/x_1, ..., v_n/x_n]\,B$ means:
+
+The expression $\btt B$ in which all occurrences of $\btt x_i$ have been replaced
+by $\btt v_i$.
+
+$\btt [v_1/x_1, ..., v_n/x_n]$ is called a \red{substitution}.
+
+Rewriting example:
+==================
+
+Consider `gcd`, the function that computes the greatest common divisor of two numbers.
+
+Here's an implementation of `gcd` using Euclid's algorithm.
+
+      def gcd(a: Int, b: Int): Int =
+        if b == 0 then a else gcd(b, a % b)
+
+Rewriting example:
+==================
+
+`gcd(14, 21)` is evaluated as follows:
+\medskip
+
+
+   `         gcd(14, 21)`
+->
+   $\rightarrow$ `  if 21 == 0 then 14 else gcd(21, 14 % 21)`
+->
+   $\rightarrow$ `  if false then 14 else gcd(21, 14 % 21)`
+->
+   $\rightarrow$ `  gcd(21, 14 % 21)`
+->
+   $\rightarrow$ `  gcd(21, 14)`
+->
+   $\rightarrow$ `  if 14 == 0 then 21 else gcd(14, 21 % 14)`
+->
+   $\rightarrow\dhd$ ` gcd(14, 7)`
+->
+   $\rightarrow\dhd$ ` gcd(7, 0)`
+->
+   $\rightarrow$ ` if 0 == 0 then 7 else gcd(0, 7 % 0)`
+->
+   $\rightarrow$ ` 7`
+
+Another rewriting example:
+==========================
+
+Consider `factorial`:
+
+      def factorial(n: Int): Int =
+        if n == 0 then 1 else n * factorial(n - 1)
+\medskip
+
+   `         factorial(4)`
+->
+   $\rightarrow$    `   if 4 == 0 then 1 else 4 * factorial(4 - 1)`
+3->
+   $\rightarrow\dhd$ `  4 * factorial(3)`
+->
+   $\rightarrow\dhd$ `  4 * (3 * factorial(2))`
+->
+   $\rightarrow\dhd$ `  4 * (3 * (2 * factorial(1)))`
+->
+   $\rightarrow\dhd$ `  4 * (3 * (2 * (1 * factorial(0)))`
+->
+   $\rightarrow\dhd$ `  4 * (3 * (2 * (1 * 1)))`
+->
+   $\rightarrow\dhd$ `  24`
+
+
+What are the differences between the two sequences?
+
+Tail Recursion
+==============
+
+\red{Implementation Consideration:}
+If a function calls itself as its last action, the function's stack frame can be reused. This is called _tail recursion_.
+
+$\Rightarrow$ Tail recursive functions are iterative processes.
+
+In general, if the last action of a function consists of calling a
+function (which may be the same), one stack frame would be sufficient for
+both functions. Such calls are called _tail-calls_.
+
+Tail Recursion in Scala
+=======================
+
+In Scala, only directly recursive calls to the current function are optimized.
+
+One can require that a function is tail-recursive using a `@tailrec` annotation:
+
+      @tailrec
+      def gcd(a: Int, b: Int): Int = ...
+
+If the annotation is given, and the implementation of `gcd` were not tail recursive, an error would be issued.
+
+
+Exercise: Tail recursion
+========================
+
+\quiz
+
+Design a tail recursive version of \verb@factorial@.
+
diff --git a/lectures/progfun1-2-1.md b/lectures/progfun1-2-1.md
new file mode 100644
index 0000000000000000000000000000000000000000..50fe7290f5ca6258ba2bb297eea6c8faf64918fc
--- /dev/null
+++ b/lectures/progfun1-2-1.md
@@ -0,0 +1,153 @@
+% Higher-Order Functions
+%
+%
+Higher-Order Functions
+======================
+
+Functional languages treat functions as _first-class values_.
+
+This means that, like any other value, a function
+can be passed as a parameter and returned as a result.
+
+This provides a flexible way to compose programs.
+
+Functions that take other functions as parameters or that return functions
+as results are called _higher order functions_.
+
+Example:
+========
+
+Take the sum of the integers between `a` and `b`:
+
+      def sumInts(a: Int, b: Int): Int =
+        if a > b then 0 else a + sumInts(a + 1, b)
+
+Take the sum of the cubes of all the integers between `a`
+and `b` :
+
+      def cube(x: Int): Int = x * x * x
+
+      def sumCubes(a: Int, b: Int): Int =
+        if a > b then 0 else cube(a) + sumCubes(a + 1, b)
+
+Example (ctd)
+=============
+
+Take the sum of the factorials of all the integers between `a`
+and `b` :
+
+      def sumFactorials(a: Int, b: Int): Int =
+        if a > b then 0 else fact(a) + sumFactorials(a + 1, b)
+
+
+These are special cases of
+$$
+   \sum^b_{n=a} f(n)
+$$
+for different values of $f$.
+
+Can we factor out the common pattern?
+
+Summing with Higher-Order Functions
+===================================
+
+Let's define:
+
+      def sum(f: Int => Int, a: Int, b: Int): Int =
+        if a > b then 0
+        else f(a) + sum(f, a + 1, b)
+
+We can then write:
+
+      def sumInts(a: Int, b: Int)       = sum(id, a, b)
+      def sumCubes(a: Int, b: Int)      = sum(cube, a, b)
+      def sumFactorials(a: Int, b: Int) = sum(fact, a, b)
+
+where
+
+      def id(x: Int): Int   = x
+      def cube(x: Int): Int = x * x * x
+      def fact(x: Int): Int = if x == 0 then 1 else x * fact(x - 1)
+
+Function Types
+==============
+
+The type `A => B` is the type of a \red{function} that
+takes an argument of type `A` and returns a result of
+type `B`.
+
+So, `Int => Int` is the type of functions that map integers to integers.
+
+Anonymous Functions
+===================
+
+Passing functions as parameters leads to the creation of many small functions.
+
+ - Sometimes it is tedious to have to define (and name) these functions using `def`.
+
+Compare to strings: We do not need to define a string using `def`. Instead of
+
+       def str = "abc"; println(str)
+
+We can directly write
+
+       println("abc")
+
+because strings exist as _literals_. Analogously we would like function literals, which let us write a function without giving it a name.
+
+These are called _anonymous functions_.
+
+Anonymous Function Syntax
+=========================
+
+\example: A function that raises its argument to a cube:
+
+      (x: Int) => x * x * x
+
+Here, `(x: Int)` is the \red{parameter} of the function, and
+`x * x * x` is it's \red{body}.
+
+- The type of the parameter can be omitted if it can be inferred by the
+compiler from the context.
+
+If there are several parameters, they are separated by commas:
+
+      (x: Int, y: Int) => x + y
+
+Anonymous Functions are Syntactic Sugar
+=======================================
+
+An anonymous function $(\btt x_1: T_1, ..., x_n: T_n) \Rightarrow E$
+can always be expressed using `def` as follows:
+
+$$
+\btt { def\ f(x_1: T_1, ..., x_n: T_n) = E ; f }
+$$
+
+where $\btt f$ is an arbitrary, fresh name (that's not yet used in the program).
+
+ - One can therefore say that anonymous functions are _syntactic sugar_.
+
+Summation with Anonymous Functions
+==================================
+
+Using anonymous functions, we can write sums in a shorter way:
+
+      def sumInts(a: Int, b: Int)  = sum(x => x, a, b)
+      def sumCubes(a: Int, b: Int) = sum(x => x * x * x, a, b)
+
+Exercise
+========
+
+\quiz
+
+The `sum` function uses linear recursion. Write a tail-recursive version by replacing the `???`s.
+
+      def sum(f: Int => Int, a: Int, b: Int): Int = {
+        def loop(a: Int, acc: Int): Int = {
+          if ??? then ???
+          else loop(???, ???)
+        }
+        loop(???, ???)
+      }
+
diff --git a/lectures/progfun1-2-2.md b/lectures/progfun1-2-2.md
new file mode 100644
index 0000000000000000000000000000000000000000..dd1c7d2954a27322c8304445ce534f8d71a00744
--- /dev/null
+++ b/lectures/progfun1-2-2.md
@@ -0,0 +1,165 @@
+% Currying
+%
+%
+Motivation
+==========
+
+Look again at the summation functions:
+
+      def sumInts(a: Int, b: Int)       = sum(x => x, a, b)
+      def sumCubes(a: Int, b: Int)      = sum(x => x * x * x, a, b)
+      def sumFactorials(a: Int, b: Int) = sum(fact, a, b)
+
+\question
+
+Note that `a` and `b` get passed unchanged from `sumInts` and `sumCubes` into `sum`.
+
+Can we be even shorter by getting rid of these parameters?
+
+
+
+Functions Returning Functions
+=============================
+
+Let's rewrite `sum` as follows.
+
+      def sum(f: Int => Int): (Int, Int) => Int = {
+        def sumF(a: Int, b: Int): Int =
+          if a > b then 0
+          else f(a) + sumF(a + 1, b)
+        sumF
+      }
+
+`sum` is now a function that returns another function.
+
+The returned function `sumF` applies the given function parameter `f` and sums the results.
+
+
+Stepwise Applications
+=====================
+
+We can then define:
+
+      def sumInts       = sum(x => x)
+      def sumCubes      = sum(x => x * x * x)
+      def sumFactorials = sum(fact)
+
+These functions can in turn be applied like any other function:
+
+      sumCubes(1, 10) + sumFactorials(10, 20)
+
+Consecutive Stepwise Applications
+=================================
+
+In the previous example, can we avoid the `sumInts`, `sumCubes`, ... middlemen?
+
+Of course:
+
+      sum (cube) (1, 10)
+->
+- `sum(cube)` applies `sum` to `cube` and returns
+the _sum of cubes_ function.
+
+- `sum(cube)` is therefore equivalent to `sumCubes`.
+
+- This function is next applied to the arguments `(1, 10)`.
+->
+Generally, function application associates to the left:
+
+       sum(cube)(1, 10)   ==   (sum (cube)) (1, 10)
+
+Multiple Parameter Lists
+========================
+
+The definition of functions that return functions is so useful in
+functional programming that there is a special syntax for it in Scala.
+
+For example, the following definition of `sum` is equivalent to
+the one with the nested `sumF` function, but shorter:
+
+      def sum(f: Int => Int)(a: Int, b: Int): Int =
+        if a > b then 0 else f(a) + sum(f)(a + 1, b)
+
+Expansion of Multiple Parameter Lists
+=====================================
+
+In general, a definition of a function with multiple parameter lists
+
+$$\btt
+      def\ f (args_1) ... (args_n) = E
+$$
+
+where $\btt n > 1$, is equivalent to
+$$\btt
+      def\ f (args_1) ... (args_{n-1}) = \{ def\ g (args_n) = E ; g \}
+$$
+
+where `g` is a fresh identifier.
+Or for short:
+
+$$\btt
+      def\ f (args_1) ... (args_{n-1}) = ( args_n \Rightarrow E )
+$$
+
+Expansion of Multiple Parameter Lists (2)
+=========================================
+
+By repeating the process $n$ times
+$$\btt
+      def\ f (args_1) ... (args_{n-1}) (args_n) = E
+$$
+is shown to be equivalent to
+$$\btt
+      def\ f = (args_1 \Rightarrow ( args_2 \Rightarrow ... ( args_n \Rightarrow E ) ... ))
+$$
+
+This style of definition and function application is called _currying_,
+named for its instigator, Haskell Brooks Curry (1900-1982), a twentieth century logician.
+
+In fact, the idea goes back even further to Schönfinkel and Frege, but the term ``currying" has stuck.
+
+More Function Types
+===================
+
+Question: Given,
+
+      def sum(f: Int => Int)(a: Int, b: Int): Int = ...
+
+What is the type of `sum` ?
+
+More Function Types
+===================
+
+Question: Given,
+
+      def sum(f: Int => Int)(a: Int, b: Int): Int = ...
+
+What is the type of `sum` ?
+
+\boldred{Answer:}
+
+      (Int => Int) => (Int, Int) => Int
+
+Note that functional types associate to the right. That is to say that
+
+        Int => Int => Int
+
+is equivalent to
+
+        Int => (Int => Int)
+
+Exercise
+========
+
+\quiz
+
+ 1. Write a `product` function that calculates the product of
+    the values of a function for the points on a given interval.
+
+ 2. Write `factorial` in terms of `product`.
+
+ 3. Can you write a more general function, which generalizes
+    both `sum` and `product`?
+
+
+
diff --git a/lectures/progfun1-2-3.md b/lectures/progfun1-2-3.md
new file mode 100644
index 0000000000000000000000000000000000000000..79bd586ac9f33eea384a40da7b6b3e847e4f26f4
--- /dev/null
+++ b/lectures/progfun1-2-3.md
@@ -0,0 +1,154 @@
+% Example: Finding Fixed Points
+%
+%
+Finding a fixed point of a function
+===================================
+
+A number `x` is called a \red{fixed point} of a function `f` if
+
+      f(x) = x
+
+For some functions `f` we can locate the fixed points by
+starting with an initial estimate and then by applying `f` in a repetitive way.
+
+      x, f(x), f(f(x)), f(f(f(x))), ...
+
+until the value does not vary anymore (or the change is sufficiently
+small).
+
+Programmatic Solution
+=====================
+
+This leads to the following function for finding a fixed point:
+
+      val tolerance = 0.0001
+      def isCloseEnough(x: Double, y: Double) =
+        abs((x - y) / x) / x < tolerance
+      def fixedPoint(f: Double => Double)(firstGuess: Double) = {
+        def iterate(guess: Double): Double = {
+          val next = f(guess)
+          if isCloseEnough(guess, next) then next
+          else iterate(next)
+        }
+        iterate(firstGuess)
+      }
+
+Return to Square Roots
+======================
+
+Here is a _specification_ of the `sqrt` function:
+
+$\gap$ `sqrt(x)` $=$ the number `y` such that `y * y = x`.
+
+Or, by dividing both sides of the equation with `y`:
+
+$\gap$ `sqrt(x)` $=$ the number `y` such that  `y = x / y`.
+
+Consequently, `sqrt(x)` is a fixed point of the function `(y => x / y)`.
+
+First Attempt
+=============
+
+This suggests to calculate `sqrt(x)` by iteration towards a fixed point:
+
+      def sqrt(x: Double) =
+        fixedPoint(y => x / y)(1.0)
+
+Unfortunately, this does not converge.
+
+Let's add a `println` instruction to the function `fixedPoint` so
+we can follow the current value of `guess`:
+
+First Attempt (2)
+=================
+
+      def fixedPoint(f: Double => Double)(firstGuess: Double) =
+
+        def iterate(guess: Double): Double =
+          val next = f(guess)
+          println(next)
+          if isCloseEnough(guess, next) then next
+          else iterate(next)
+
+        iterate(firstGuess)
+
+`sqrt(2)` then produces:
+
+        2.0
+        1.0
+        2.0
+        1.0
+        ...
+
+Average Damping
+===============
+
+One way to control such oscillations is to prevent the estimation from
+varying too much.  This is done by _averaging_ successive values of the
+original sequence:
+
+       def sqrt(x: Double) = fixedPoint(y => (y + x / y) / 2)(1.0)
+
+This produces
+
+      1.5
+      1.4166666666666665
+      1.4142156862745097
+      1.4142135623746899
+      1.4142135623746899
+
+In fact, if we expand the fixed point function `fixedPoint` we find a similar
+square root function to what we developed last week.
+
+Functions as Return Values
+==========================
+
+The previous examples have shown that the expressive power of a
+language is greatly increased if we can pass function arguments.
+
+The following example shows that functions that return functions
+can also be very useful.
+
+Consider again iteration towards a fixed point.
+
+We begin by observing that $\sqrt x$ is a fixed point of the function
+`y => x / y`.
+
+Then, the iteration converges by averaging successive values.
+
+This technique of _stabilizing by averaging_ is general enough to
+merit being abstracted into its own function.
+
+      def averageDamp(f: Double => Double)(x: Double) = (x + f(x)) / 2
+
+Exercise:
+=========
+
+Write a square root function using
+`fixedPoint` and `averageDamp`.
+
+\quiz
+
+Final Formulation of Square Root
+================================
+
+      def sqrt(x: Double) = fixedPoint (averageDamp (y => x/y)) (1.0)
+
+This expresses the elements of the algorithm as clearly as possible.
+
+Summary
+=======
+
+We saw last week that the functions are essential abstractions because
+they allow us to introduce general methods to perform computations as
+explicit and named elements in our programming language.
+
+This week, we've seen that these abstractions can be combined with
+higher-order functions to create new abstractions.
+
+As a programmer, one must look for opportunities to abstract and
+reuse.
+
+The highest level of abstraction is not always the best, but it
+is important to know the techniques of abstraction, so as to
+use them when appropriate.
diff --git a/lectures/progfun1-2-4.md b/lectures/progfun1-2-4.md
new file mode 100644
index 0000000000000000000000000000000000000000..ed92d4d01c8017401908cacb68138eb42bf6abed
--- /dev/null
+++ b/lectures/progfun1-2-4.md
@@ -0,0 +1,94 @@
+% Scala Syntax Summary
+%
+%
+
+Language Elements Seen So Far:
+==============================
+
+We have seen language elements to express types, expressions
+and definitions.
+
+Below, we give their context-free syntax in Extended Backus-Naur form (EBNF),
+where
+
+$\gap$   `|` denotes an alternative, \par
+
+$\gap$   `[...]` an option (0 or 1), \par
+
+$\gap$   `{...}` a repetition (0 or more).
+
+Types
+=====
+
+\begin{lstlisting}
+Type         = SimpleType | FunctionType
+FunctionType = SimpleType `=>' Type
+             | `(' [Types] `)' `=>' Type
+SimpleType   = Ident
+Types        = Type {`,' Type}
+\end{lstlisting}
+
+A \red{type} can be:
+
+ - A \red{numeric type}: `Int`, `Double` (and `Byte`, `Short`, `Char`, `Long`, `Float`),
+ - The `Boolean` type with the values `true` and `false`,
+ - The `String` type,
+ - A \red{function type}, like `Int => Int`, `(Int, Int) => Int`.
+
+Later we will see more forms of types.
+
+Expressions
+===========
+
+\begin{lstlisting}
+Expr         = InfixExpr | FunctionExpr
+             | if Expr then Expr else Expr
+InfixExpr    = PrefixExpr | InfixExpr Operator InfixExpr
+Operator     = ident
+PrefixExpr   = [`+' | `-' | `!' | `~' ] SimpleExpr
+SimpleExpr   = ident | literal | SimpleExpr `.' ident
+             | Block
+FunctionExpr = Bindings `=>` Expr
+Bindings     = ident
+             | `(' [Binding {`,' Binding}] `)'
+Binding      = ident [`:' Type]
+Block        = `{' {Def `;'} Expr `}'
+             | <indent> {Def `;'} Expr <outdent>
+\end{lstlisting}
+
+Expressions (2)
+===============
+
+An \red{expression} can be:
+
+- An \red{identifier} such as `x`, `isGoodEnough`,
+- A \red{literal}, like `0`, `1.0`, `"abc"`,
+- A \red{function application}, like `sqrt(x)`,
+- An \red{operator application}, like `-x`, `y + x`,
+- A \red{selection}, like `math.abs`,
+- A \red{conditional expression}, like `if x < 0 then -x else x`,
+- A \red{block}, like `{ val x = math.abs(y) ; x * 2 }`
+- An \red{anonymous function}, like `x => x + 1`.
+
+Definitions
+===========
+
+\begin{lstlisting}
+Def          = FunDef  |  ValDef
+FunDef       = def ident {`(' [Parameters] `)'}
+               [`:' Type] `=' Expr
+ValDef       = val ident [`:' Type] `=' Expr
+Parameter    = ident `:' [ `=>' ] Type
+Parameters   = Parameter {`,' Parameter}
+\end{lstlisting}
+
+A \red{definition} can be:
+
+- A \red{function definition}, like `def square(x: Int) = x * x`
+- A \red{value definition}, like `val y = square(2)`
+
+A \red{parameter} can be:
+
+- A \red{call-by-value parameter}, like `(x: Int)`,
+- A \red{call-by-name parameter}, like `(y: => Double)`.
+
diff --git a/lectures/progfun1-2-5.md b/lectures/progfun1-2-5.md
new file mode 100644
index 0000000000000000000000000000000000000000..837cf3c4dce0919ecfc112c973a51bb92452eeaa
--- /dev/null
+++ b/lectures/progfun1-2-5.md
@@ -0,0 +1,175 @@
+% Functions and Data
+%
+%
+
+Functions and Data
+==================
+
+In this section, we'll learn how functions create and encapsulate data
+structures.
+
+\example
+
+Rational Numbers
+
+We want to design a package for doing rational arithmetic.
+
+A rational number $\blue{\frac{x}{y}}$ is represented by two integers:
+
+ - its _numerator_ $\blue{x}$, and
+ - its _denominator_ $\blue{y}$.
+
+Rational Addition
+=================
+
+Suppose we want to implement the addition of two rational numbers.
+
+      def addRationalNumerator(n1: Int, d1: Int, n2: Int, d2: Int): Int
+      def addRationalDenominator(n1: Int, d1: Int, n2: Int, d2: Int): Int
+
+but it would be difficult to manage all these numerators and denominators.
+
+A better choice is to combine the numerator and
+denominator of a rational number in a data structure.
+
+Classes
+=======
+
+In Scala, we do this by defining a \red{class}:
+
+      class Rational(x: Int, y: Int)
+        def numer = x
+        def denom = y
+
+This definition introduces two entities:
+
+ - A new \red{type}, named `Rational`.
+ - A \red{constructor} `Rational` to create elements of this type.
+
+Scala keeps the names of types and values in \red{different namespaces}.
+So there's no conflict between the two defintions of `Rational`.
+
+Objects
+=======
+
+We call the elements of a class type \red{objects}.
+
+We create an object by calling the constructor of the class:
+
+\example
+
+      Rational(1, 2)
+
+Members of an Object
+====================
+
+Objects of the class `Rational` have two \red{members},
+`numer` and `denom`.
+
+We select the members of an object with the infix operator `.' (like in Java).
+
+\example
+
+\begin{tabular}{ll}
+  \verb@val x = Rational(1, 2)@  \wsf x: Rational = Rational@2abe0e27
+\\
+  x.numer \wsf 1
+\\
+  x.denom  \wsf 2
+\end{tabular}
+
+Rational Arithmetic
+===================
+
+We can now define the arithmetic functions that implement the standard rules.
+
+$$
+\ba{l}
+\frac{n_1}{d_1} + \frac{n_2}{d_2} \ = \ \frac{n_1 d_2 + n_2 d_1}{d_1 d_2}
+\\[1em]
+\frac{n_1}{d_1} - \frac{n_2}{d_2} \ = \ \frac{n_1 d_2 - n_2 d_1}{d_1
+d_2}
+\\[1em]
+\frac{n_1}{d_1} \cdot \frac{n_2}{d_2} \ = \ \frac{n_1 n_2}{d_1 d_2}
+\\[1em]
+\frac{n_1}{d_1} / \frac{n_2}{d_2} \ = \ \frac{n_1 d_2}{d_1 n_2}
+\\[1em]
+\frac{n_1}{d_1} = \frac{n_2}{d_2} \quad \small \mbox{iff} \quad n_1 d_2 = d_1 n_2
+\ea
+$$
+
+Implementing Rational Arithmetic
+================================
+
+      def addRational(r: Rational, s: Rational): Rational =
+        Rational(
+          r.numer * s.denom + s.numer * r.denom,
+          r.denom * s.denom)
+
+      def makeString(r: Rational) =
+        r.numer + "/" + r.denom
+
+\begin{worksheet}
+      \verb@makeString(addRational(new Rational(1, 2), new Rational(2, 3)))@ \wsf       7/6
+\end{worksheet}
+
+
+Methods
+=======
+
+One can go further and also package functions operating on a data
+abstraction in the data abstraction itself.
+
+Such functions are called \red{methods}.
+
+\example
+
+Rational numbers now would have, in addition to the functions `numer`
+and `denom`, the functions  `add`, `sub`,
+`mul`, `div`, `equal`, `toString`.
+
+Methods for Rationals
+=====================
+
+Here's a possible implementation:
+
+    class Rational(x: Int, y: Int) {
+      def numer = x
+      def denom = y
+      def add(r: Rational) =
+        new Rational(numer * r.denom + r.numer * denom,
+                     denom * r.denom)
+      def mul(r: Rational) = ...
+      ...
+      override def toString = numer + "/" + denom
+    }
+
+\red{Remark}: the modifier `override` declares that `toString`
+redefines a method that already exists (in the class `java.lang.Object`).
+
+Calling Methods
+===============
+
+
+Here is how one might use the new `Rational` abstraction:
+
+      val x = new Rational(1, 3)
+      val y = new Rational(5, 7)
+      val z = new Rational(3, 2)
+      x.add(y).mul(z)
+
+Exercise
+========
+
+1. In your worksheet, add a method `neg` to class Rational that is used like this: \medskip
+
+          x.neg          // evaluates to -x
+
+2. Add a method `sub` to subtract two rational numbers.
+
+3. With the values of `x`, `y`, `z` as given in the previous slide, what is the result of
+\medskip
+
+          x - y - z
+
+\ \ \ ?
diff --git a/lectures/progfun1-2-6.md b/lectures/progfun1-2-6.md
new file mode 100644
index 0000000000000000000000000000000000000000..42c6cd08d6ea96126cc793328ca72d733ec621fd
--- /dev/null
+++ b/lectures/progfun1-2-6.md
@@ -0,0 +1,189 @@
+% More Fun with Rationals
+% \
+% \
+
+Data Abstraction
+================
+
+The previous example has shown that rational numbers aren't always
+represented in their simplest form. (Why?)
+
+One would expect the rational numbers to be _simplified_:
+
+- reduce them to their smallest numerator and denominator by dividing both with a divisor.
+
+We could implement this in each rational operation, but it would be easy
+to forget this division in an operation.
+
+A better alternative consists of simplifying the representation in
+the class when the objects are constructed:
+
+Rationals with Data Abstraction
+===============================
+
+      class Rational(x: Int, y: Int) {
+        private def gcd(a: Int, b: Int): Int = if b == 0 then a else gcd(b, a % b)
+        private val g = gcd(x, y)
+        def numer = x / g
+        def denom = y / g
+        ...
+      }
+
+`gcd` and `g` are \red{private} members; we can only access them
+from inside the `Rational` class.
+
+In this example, we calculate `gcd` immediately, so that its value can be re-used
+in the calculations of `numer` and `denom`.
+
+Rationals with Data Abstraction (2)
+===================================
+
+It is also possible to call `gcd` in the code of
+`numer` and `denom`:
+
+      class Rational(x: Int, y: Int) {
+        private def gcd(a: Int, b: Int): Int = if b == 0 then a else gcd(b, a % b)
+        def numer = x / gcd(x, y)
+        def denom = y / gcd(x, y)
+      }
+
+This can be advantageous if it is expected that the functions `numer`
+and `denom` are called infrequently.
+
+Rationals with Data Abstraction (3)
+===================================
+
+It is equally possible to turn `numer` and `denom` into `val`s, so that they are computed only once:
+
+      class Rational(x: Int, y: Int) {
+        private def gcd(a: Int, b: Int): Int = if b == 0 then a else gcd(b, a % b)
+        val numer = x / gcd(x, y)
+        val denom = y / gcd(x, y)
+      }
+
+This can be advantageous if the functions `numer` and `denom` are called often.
+
+The Client's View
+=================
+
+Clients observe exactly the same behavior in each case.
+
+This ability to choose different implementations of the data without affecting
+clients is called \red{data abstraction}.
+
+It is a cornerstone of software engineering.
+
+Self Reference
+==============
+
+On the inside of a class, the name `this` represents the object
+on which the current method is executed.
+
+\example
+
+Add the functions `less` and `max` to the class `Rational`.
+
+      class Rational(x: Int, y: Int) {
+        ...
+        def less(that: Rational) =
+          numer * that.denom < that.numer * denom
+
+        def max(that: Rational) =
+          if this.less(that) then that else this
+      }
+
+Self Reference (2)
+==================
+
+Note that a simple name `x`, which refers to another member
+of the class, is an abbreviation of `this.x`. Thus, an equivalent
+way to formulate `less` is as follows.
+
+      def less(that: Rational) =
+        this.numer * that.denom < that.numer * this.denom
+
+Preconditions
+=============
+
+Let's say our `Rational` class requires that the denominator is positive.
+
+We can enforce this by calling the `require` function.
+
+      class Rational(x: Int, y: Int) {
+        require(y > 0, "denominator must be positive")
+        ...
+      }
+
+`require` is a predefined function.
+
+It takes a condition and an optional message string.
+
+If the condition passed to `require` is `false`, an `IllegalArgumentException` is thrown with the given message string.
+
+Assertions
+==========
+
+Besides `require`, there is also `assert`.
+
+Assert also takes a condition and an optional message string as parameters. E.g.
+
+      val x = sqrt(y)
+      assert(x >= 0)
+
+Like `require`, a failing `assert` will also throw an exception, but it's a different one: `AssertionError` for `assert`, `IllegalArgumentException` for `require`.
+
+This reflects a difference in intent
+
+ - `require` is used to enforce a precondition on the caller of a function.
+ - `assert` is used as to check the code of the function itself.
+
+Constructors
+============
+
+In Scala, a class implicitly introduces a constructor. This one
+is called the \red{primary constructor} of the class.
+
+The primary constructor
+
+ - takes the parameters of the class
+ - and executes all statements in the class body
+   (such as the `require` a couple of slides back).
+
+Auxiliary Constructors
+======================
+
+Scala also allows the declaration of \red{auxiliary constructors}.
+
+These are methods named `this`
+
+\example Adding an auxiliary constructor to the class `Rational`.
+
+      class Rational(x: Int, y: Int) {
+        def this(x: Int) = this(x, 1)
+        ...
+      }
+
+\begin{worksheet}
+      new Rational(2)   \wsf   2/1
+\end{worksheet}
+
+
+Exercise
+========
+
+Modify the `Rational` class so that rational numbers are kept unsimplified internally,
+but the simplification is applied when numbers are converted to strings.
+
+Do clients observe the same behavior when interacting with the rational class? \bigskip
+
+\begin{tabular}{lp{8cm}}
+   \verb@      O      @ & yes
+\\ \verb@      O      @ & no
+\\ \verb@      O      @ & yes for small sizes of denominators and nominators and small numbers of operations.
+\end{tabular}
+
+
+
+
+
+
diff --git a/lectures/progfun1-2-7.md b/lectures/progfun1-2-7.md
new file mode 100644
index 0000000000000000000000000000000000000000..60bffd35e14a40e75b99fc3dc42ce13b11df9a33
--- /dev/null
+++ b/lectures/progfun1-2-7.md
@@ -0,0 +1,185 @@
+% Evaluation and Operators
+%
+%
+
+Classes and Substitutions
+=========================
+
+We previously defined the meaning of a function application using
+a computation model based on substitution. Now we extend this
+model to classes and objects.
+
+\red{Question:} How is an instantiation of the class
+$\btt new\ C(e_1, ..., e_m)$ evaluted?
+
+\red{Answer:} The expression arguments $\btt e_1, ..., e_m$
+are evaluated like the arguments of a normal function. That's it.
+
+The resulting expression, say, $\btt new\ C(v_1, ..., v_m)$, is
+already a value.
+
+Classes and Substitutions
+=========================
+
+Now suppose that we have a class definition,
+
+$$\btt
+      class\ C(x_1, ..., x_m) \{\ ...\ def\ f(y_1, ..., y_n) = b\ ...\ \}
+$$
+where
+
+ - The formal parameters of the class are $\btt x_1, ..., x_m$.
+ - The class defines a method $\btt f$ with formal parameters
+   $\btt y_1, ..., y_n$.
+
+(The list of function parameters can be absent. For simplicity, we
+have omitted the parameter types.)
+
+\red{Question:} How is the following expression evaluated?
+$$\btt
+        new\ C(v_1, ..., v_m).f(w_1, ..., w_n)
+$$
+
+Classes and Substitutions (2)
+=============================
+
+\red{Answer:} The expression $\btt new\ C(v_1, ..., v_m).f(w_1, ..., w_n)$ is rewritten to:
+$$\btt
+[w_1/y_1, ..., w_n/y_n]
+  [v_1/x_1, ..., v_m/x_m]
+    [new\ C(v_1, ..., v_m)/this]\,b
+$$
+There are three substitutions at work here:
+
+ - the substitution of the formal parameters $\btt y_1, ..., y_n$ of the function $\btt f$ by the
+arguments $\btt w_1, ..., w_n$,
+ - the substitution of the formal parameters $\btt x_1, ..., x_m$ of the class $\btt C$ by the class arguments
+$\btt v_1, ..., v_m$,
+ - the substitution of the self reference $this$ by the value of the object $\btt new\ C(v_1, ..., v_n)$.
+
+Object Rewriting Examples
+=========================
+
+   `      new Rational(1, 2).numer`
+->
+   $\rightarrow$  $\btt [1/x, 2/y]\ []\ [new\ Rational(1, 2)/this]$ `x`
+->
+   =$\,$  `1`
+\medskip
+->
+  `      new Rational(1, 2).less(new Rational(2, 3))`
+->
+   $\rightarrow$ $\btt [1/x, 2/y]\ [new Rational(2, 3)/that]\ [new\ Rational(1, 2)/this]$
+
+\nl\gap `this.numer * that.denom < that.numer * this.denom`
+->
+   =$\,$  `new Rational(1, 2).numer * new Rational(2, 3).denom <`
+
+\nl\gap `new Rational(2, 3).numer * new Rational(1, 2).denom`
+->
+   $\rightarrow\dhd$  `1 * 3 < 2 * 2`
+
+   $\rightarrow\dhd$  `true`
+
+
+Operators
+=========
+
+In principle, the rational numbers defined by `Rational` are
+as natural as integers.
+
+But for the user of these abstractions, there is a noticeable
+difference:
+
+ - We write `x + y`, if `x` and `y` are integers, but
+ - We write `r.add(s)` if `r` and `s` are rational numbers.
+
+In Scala, we can eliminate this difference. We proceed in two steps.
+
+Step 1: Infix Notation
+======================
+
+Any method with a parameter can be used like an infix operator.
+
+It is therefore possible to write
+
+      r add s                                         r.add(s)
+      r less s          /* in place of */             r.less(s)
+      r max s                                         r.max(s)
+
+Step 2: Relaxed Identifiers
+===========================
+
+Operators can be used as identifiers.
+
+Thus, an identifier can be:
+
+ - \red{Alphanumeric}: starting with a letter, followed by a sequence of letters or numbers
+ - \red{Symbolic}: starting with an operator symbol, followed by other operator symbols.
+ - The underscore character `'_'` counts as a letter.
+ - Alphanumeric identifiers can also end in an underscore, followed by some operator symbols.
+
+Examples of identifiers:
+
+      x1     *     +?%&     vector_++     counter_=
+
+
+
+
+
+Operators for Rationals
+=======================
+
+A more natural definition of class `Rational`:
+
+      class Rational(x: Int, y: Int) {
+        private def gcd(a: Int, b: Int): Int = if b == 0 then a else gcd(b, a % b)
+        private val g = gcd(x, y)
+        def numer = x / g
+        def denom = y / g
+        def + (r: Rational) =
+          new Rational(
+            numer * r.denom + r.numer * denom,
+            denom * r.denom)
+        def - (r: Rational) = ...
+        def * (r: Rational) = ...
+        ...
+      }
+
+Operators for Rationals
+=======================
+
+... and rational numbers can be used like `Int` or `Double`:
+
+      val x = new Rational(1, 2)
+      val y = new Rational(1, 3)
+      x * x + y * y
+
+Precedence Rules
+================
+
+The \red{precedence} of an operator is determined by its first character.
+
+The following table lists the characters in increasing order of priority precedence:
+
+        (all letters)
+        |
+        ^
+        &
+        < >
+        = !
+        :
+        + -
+        * / %
+        (all other special characters)
+
+Exercise
+========
+
+Provide a fully parenthized version of
+
+     a + b ^? c ?^ d less a ==> b | c
+
+Every binary operation needs to be put into parentheses,
+but the structure of the expression should not change.
+
diff --git a/lectures/progfun1-2-new/progfun1-2-1.md b/lectures/progfun1-2-new/progfun1-2-1.md
new file mode 100644
index 0000000000000000000000000000000000000000..f1418b6e22640df2b7829903c84498ea3aa3b9c6
--- /dev/null
+++ b/lectures/progfun1-2-new/progfun1-2-1.md
@@ -0,0 +1,173 @@
+% Functions and Data
+%
+%
+
+Functions and Data
+==================
+
+In this section, we'll learn how functions create and encapsulate data
+structures.
+
+\example
+
+Rational Numbers
+
+We want to design a package for doing rational arithmetic.
+
+A rational number $\blue{\frac{x}{y}}$ is represented by two integers:
+
+ - its _numerator_ $\blue{x}$, and
+ - its _denominator_ $\blue{y}$.
+
+Rational Addition
+=================
+
+Suppose we want to implement the addition of two rational numbers.
+
+      def addRationalNumerator(n1: Int, d1: Int, n2: Int, d2: Int): Int
+      def addRationalDenominator(n1: Int, d1: Int, n2: Int, d2: Int): Int
+
+but it would be difficult to manage all these numerators and denominators.
+
+A better choice is to combine the numerator and
+denominator of a rational number in a data structure.
+
+Case Classes
+============
+
+In Scala, we do this by defining a \red{case class}:
+
+~~~
+case class Rational(numer: Int, denom: Int)
+~~~
+
+This definition introduces two entities:
+
+ - A new \red{type}, named `Rational`.
+ - A \red{constructor} `Rational` to create elements of this type.
+
+Scala keeps the names of types and values in \red{different namespaces}.
+So there's no conflict between the two definitions of `Rational`.
+
+Objects
+=======
+
+We call the elements of a class type \red{objects}.
+
+We create an object by calling the constructor of the class.
+
+\example
+
+       Rational(1, 2)
+
+Members of an Object
+====================
+
+Objects of the class `Rational` have two \red{members},
+`numer` and `denom`.
+
+We select the members of an object with the infix operator `.' (like in Java).
+
+\example
+
+\begin{tabular}{ll}
+  \verb@val x = Rational(1, 2)@  \wsf x: Rational = Rational@2abe0e27
+\\
+  x.numer \wsf 1
+\\
+  x.denom  \wsf 2
+\end{tabular}
+
+Rational Arithmetic
+===================
+
+We can now define the arithmetic functions that implement the standard rules.
+
+$$
+\ba{l}
+\frac{n_1}{d_1} + \frac{n_2}{d_2} \ = \ \frac{n_1 d_2 + n_2 d_1}{d_1 d_2}
+\\[1em]
+\frac{n_1}{d_1} - \frac{n_2}{d_2} \ = \ \frac{n_1 d_2 - n_2 d_1}{d_1
+d_2}
+\\[1em]
+\frac{n_1}{d_1} \cdot \frac{n_2}{d_2} \ = \ \frac{n_1 n_2}{d_1 d_2}
+\\[1em]
+\frac{n_1}{d_1} / \frac{n_2}{d_2} \ = \ \frac{n_1 d_2}{d_1 n_2}
+\\[1em]
+\frac{n_1}{d_1} = \frac{n_2}{d_2} \quad \small \mbox{iff} \quad n_1 d_2 = d_1 n_2
+\ea
+$$
+
+Implementing Rational Arithmetic
+================================
+
+      def addRational(r: Rational, s: Rational): Rational =
+        Rational(
+          r.numer * s.denom + s.numer * r.denom,
+          r.denom * s.denom)
+
+      def makeString(r: Rational) =
+        s"${r.numer}/${r.denom}"
+
+\begin{worksheet}
+      \verb@makeString(addRational(Rational(1, 2), Rational(2, 3)))@ \wsf       7/6
+\end{worksheet}
+
+
+Methods
+=======
+
+One can go further and also package functions operating on a data
+abstraction in the data abstraction itself.
+
+Such functions are called \red{methods}.
+
+\example
+
+Rational numbers now would have, in addition to the functions `numer`
+and `denom`, the functions  `add`, `sub`,
+`mul`, `div`, `equal`, `toString`.
+
+Methods for Rationals
+=====================
+
+Here's a possible implementation:
+
+    case class Rational(numer: Int, denom: Int)
+      def add(r: Rational) =
+        Rational(numer * r.denom + r.numer * denom,
+                 denom * r.denom)
+      def mul(r: Rational) = ...
+      ...
+      override def toString = s"$numer/$denom"
+    end Rational
+
+\red{Remark}: the modifier `override` declares that `toString`
+redefines a method that already exists (in the class `java.lang.Object`).
+
+Calling Methods
+===============
+
+
+Here is how one might use the new `Rational` abstraction:
+
+      val x = Rational(1, 3)
+      val y = Rational(5, 7)
+      val z = Rational(3, 2)
+      x.add(y).mul(z)
+
+Exercise
+========
+
+1. In your worksheet, add a method `neg` to class Rational that is used like this: \medskip
+
+          x.neg          // evaluates to -x
+
+2. Add a method `sub` to subtract two rational numbers.
+
+3. With the values of `x`, `y`, `z` as given in the previous slide, what is the result of
+\medskip
+
+          x - y - z
+
+\ \ \ ?
diff --git a/lectures/progfun1-2-new/progfun1-2-2.md b/lectures/progfun1-2-new/progfun1-2-2.md
new file mode 100644
index 0000000000000000000000000000000000000000..2a94488ec178839824beb317508b7bb62bf9428e
--- /dev/null
+++ b/lectures/progfun1-2-new/progfun1-2-2.md
@@ -0,0 +1,328 @@
+% Enums
+%
+%
+
+Enums: Motivation
+=================
+
+We have seen that case classes *aggregate several values* into a single abstraction.
+For instance, the `Rational` case class aggregates a numerator and a denominator.
+
+Conversely, how could we define an abstraction *accepting alternative values*?
+
+\example
+
+Define a `Paradigm` type that can be either `Functional` or `Imperative`.
+
+Enums
+=====
+
+We can define an abstraction accepting alternative values with an \red{enum}:
+
+~~~
+enum Paradigm
+  case Functional, Imperative
+~~~
+
+This definition introduces:
+
+- A new \red{type}, named `Paradigm`.
+- Two possible \red{values} for this type, `Paradigm.Functional` and
+  `Paradigm.Imperative`.
+
+Enumerate the Values of an Enumeration
+======================================
+
+It is possible to enumerate all the values of an enum by calling the
+`values` operation on the enum companion object:
+
+\begin{tabular}{ll}
+ \verb@Paradigm.values@              \wsf Array(Functional, Imperative) \\
+ \verb@val p = Paradigm.Functional@  \wsf p: Paradigm = Functional \\
+ \verb@p == Paradigm.values(0)@      \wsf true
+\end{tabular}
+
+Discriminate the Values of an Enumeration
+=========================================
+
+You can discriminate between the values of an enum by using a \red{match}
+expression:
+
+~~~
+def isReferentiallyTransparent(paradigm: Paradigm): Boolean =
+  paradigm match
+    case Paradigm.Functional => true
+    case Paradigm.Imperative => false
+~~~
+
+->
+
+And then:
+
+\begin{tabular}{ll}
+ \verb@val p = Paradigm.Functional@   \wsf p: Paradigm = Functional \\
+ \verb@isReferentiallyTransparent(p)@ \wsf true
+\end{tabular}
+
+Match Syntax
+============
+
+_Pattern matching_ is a generalization of `switch` from C/Java
+to class hierarchies.
+
+It’s expressed in Scala using the `match` keyword.
+
+- `match` is followed by a sequence of \red{cases}, `case value => expr`.
+- Each case associates an \red{expression} `expr` with a
+  \red{constant} `value`.
+
+We will see in the next slides that pattern matching can do more
+than discriminating enums.
+
+Enumerations Can Have Methods
+=============================
+
+Alternatively, we can define `isReferentiallyTransparent` as a method:
+
+~~~
+enum Paradigm
+  case Functional, Imperative
+
+  def isReferentiallyTransparent: Boolean = this match
+    case Functional => true
+    case Imperative => false
+~~~
+
+->
+
+And then:
+
+\begin{tabular}{ll}
+ \verb@val p = Paradigm.Functional@  \wsf p: Paradigm = Functional \\
+ \verb@p.isReferentiallyTransparent@ \wsf true
+\end{tabular}
+
+Enumerations Values Can Take Parameters
+=======================================
+
+Consider the following definition of a data type modeling students
+following a lecture:
+
+~~~
+enum Student
+  case Listening
+  case Asking(question: String)
+~~~
+
+This definition introduces a `Student` type consisting of two cases,
+`Listening` or `Asking`. The `Asking` case is parameterized with a
+value parameter `question`. Since `Listening` is not parameterized,
+it is treated as a normal enum value.
+
+Matching on Constructors
+========================
+
+Since `Asking` is not a constant, we match on it using
+a _constructor pattern_:
+
+~~~
+student match
+  case Student.Listening => "Student is listening"
+  case Student.Asking(q) => s"Student is asking: $q"
+~~~
+
+A constructor pattern allows us to _extract_ the value of
+the `question` parameter, in case the `student` value is
+indeed of type `Asking`.
+
+Here, the `q` identifier is bound to the `question` parameter
+of the `student` object.
+
+Evaluating Match Expressions
+============================
+
+An expression of the form
+$$\btt
+e\ match\ \{\ case\ p_1 => e_1\ ...\ case\ p_n => e_n\ \}
+$$
+matches the value of the selector $\btt e$ with the patterns
+$\btt p_1, ..., p_n$ in the order in which they are written.
+
+The whole match expression is rewritten to the right-hand side of the first
+case where the pattern matches the selector $e$.
+
+References to pattern variables are replaced by the corresponding
+parts in the selector.
+
+Forms of Patterns
+=================
+
+Patterns are constructed from:
+
+ -  _constructors_, e.g. `Rational`, `Student.Asking`,
+ -  _variables_, e.g. `n`, `e1`, `e2`,
+ -  _wildcard patterns_ `_`,
+ -  _constants_, e.g. `1`, `true`, `Paradigm.Functional`.
+
+Variables always begin with a _lowercase letter_.
+
+The same variable name can only appear _once_ in a pattern.
+
+Names of constants begin with a capital letter,
+with the exception of the reserved words `null`, `true`,
+`false`.
+
+What Do Patterns Match?
+=======================
+
+- A constructor pattern $\btt C(p_1 , ..., p_n)$ matches
+  all the values of type $\btt C$ (or a subtype) that have been
+  constructed with arguments matching the patterns $\btt p_1, ..., p_n$.
+- A variable pattern $\btt x$ matches any value, and
+  \red{binds} the name of the variable to this value.
+- A constant pattern $\btt c$ matches values that are equal to
+  $\btt c$ (in the sense of `==`)
+
+Matching on Case Classes
+========================
+
+Constructor patterns also works with case classes:
+
+~~~
+def invert(x: Rational): Rational =
+  x match
+    case Rational(0, _) => sys.error("Unable to invert zero")
+    case Rational(n, d) => Rational(d, n)
+~~~
+
+Relationship Between Case Classes and Parameterized Enum Cases
+==============================================================
+
+Case classes and parameterized enum cases are very similar.
+
+When should you use one or the other?
+
+- Parameterized enum cases should be used to define a type
+  that is part of a set of alternatives,
+- If there are no alternatives, just use a case class.
+
+Enumerations Can Be Recursive
+=============================
+
+A list of integer values can be modeled as either an empty list,
+or a node containing both a number and a reference to the remainder
+of the list.
+
+~~~
+enum List
+  case Empty
+  case Node(value: Int, next: List)
+~~~
+
+A list with values `1`, `2`, and `3` can be constructed as follows:
+
+~~~
+List.Node(1, List.Node(2, List.Node(3, List.Empty)))
+~~~
+
+Enumerations Can Take Parameters
+================================
+
+~~~
+enum Vehicle(val numberOfWheels: Int)
+  case Unicycle extends Vehicle(1)
+  case Bicycle  extends Vehicle(2)
+  case Car      extends Vehicle(4)
+~~~
+
+- Enumeration cases have to use an explicit `extends` clause
+
+Exercise: Arithmetic Expressions
+================================
+
+Define a datatype modeling arithmetic expressions.
+
+An expression can be:
+
+- a number (e.g. `42`),
+- a sum of two expressions,
+- or, the product of two expressions.
+
+`Expr` Type Definition
+======================
+
+~~~
+enum Expr
+  case Number(n: Int)
+  case Sum(lhs: Expr, rhs: Expr)
+  case Prod(lhs: Expr, rhs: Expr)
+
+->
+
+\begin{tabular}{ll}
+ \verb@val one = Expr.Number(1)@  \wsf one: Expr = Number(1) \\
+ \verb@val two = Expr.Number(2)@  \wsf two: Expr = Number(2) \\
+ \verb@Expr.Sum(one, two)@        \wsf Sum(Number(1), Number(2))
+\end{tabular}
+
+
+Exercise
+========
+
+Is the following match expression valid?
+
+~~~
+expr match
+  case Expr.Sum(x, x) => Expr.Prod(2, x)
+  case e              => e
+~~~
+
+     O         Yes
+     O         No
+
+Exercise
+========
+
+Implement an `eval` operation that takes an `Expr` as parameter and
+evaluates its value:
+
+~~~
+def eval(e: Expr): Int
+~~~
+
+Examples of use:
+
+\begin{tabular}{ll}
+ \verb@eval(Expr.Number(42))@  \wsf 42 \\
+ \verb@eval(Expr.Prod(2, Expr.Sum(Expr.Number(8), Expr.Number(13))))@  \wsf 42
+\end{tabular}
+
+Exercise
+========
+
+Implement a `show` operation that takes an `Expr` as parameter and
+returns a `String` representation of the operation.
+
+Be careful to introduce parenthesis when necessary!
+
+~~~
+def show(e: Expr): String
+~~~
+
+Examples of use:
+
+\begin{tabular}{ll}
+ \verb@show(Expr.Number(42))@  \wsf "42" \\
+ \verb@show(Expr.Prod(2, Expr.Sum(Expr.Number(8), Expr.Number(13))))@  \wsf "2 * (8 + 13)"
+\end{tabular}
+
+Summary
+=======
+
+In this lecture, we have seen:
+
+- how to define data types accepting alternative values using
+  `enum` definitions,
+- enumeration cases can be discriminated using pattern matching,
+- enumeration cases can take parameters or be simple values,
+- pattern matching allows us to extract information carried by an object.
diff --git a/lectures/progfun1-2-new/progfun1-2-3.md b/lectures/progfun1-2-new/progfun1-2-3.md
new file mode 100644
index 0000000000000000000000000000000000000000..348950bd3af6c4e92e0150db6d8ca56d950a71f3
--- /dev/null
+++ b/lectures/progfun1-2-new/progfun1-2-3.md
@@ -0,0 +1,115 @@
+% Business Domain Modeling
+%
+%
+
+Business Domain Modeling
+========================
+
+Enums and case classes together provide basic building blocks
+for defining data types.
+
+They are typically used to **model the business domain** of a program.
+
+How to Model Things?
+====================
+
+Modeling consists of translating concepts from the real world
+into data type definitions in the Scala world.
+
+Modeling requires you to think about what details of the real
+world are relevant for your program.
+
+> The purpose of abstraction is not to be vague, but to create
+> a new semantic level in which one can be absolutely precise.
+> 
+> Edsger W. Dijkstra
+
+
+Modeling Methodology (1)
+========================
+
+There is no systematic methodology: often, a same set of concepts
+can be modeled in multiple ways.
+
+But here are some advice.
+
+->
+
+- Identify the concepts (in general, nouns) that you are interested in
+- Identify the relations between them
+    - Does a concept _belong to_ another one?
+        - e.g. “a rational number _has_ a numerator and a denominator”
+        - e.g. “a sum _has_ a left summand and a right summand”
+    - Does a concept _generalize_ another one?
+        - e.g. “‘functional programming’ _is_ a possible programming paradigm”
+        - e.g. “an arithmetic operation can either _be_ a product or a sum”
+
+Modeling Methodology (2)
+========================
+
+- Translate each concept into a type definition
+    - Concepts belonging to others become **fields** of case classes
+      or of enumeration cases
+    - Concepts generalizing others become **enumerations**
+- Check that you can construct meaningful objects from your model
+
+Example: Painting Application
+=============================
+
+An image is made of a shape, a stroke width and a color.
+
+Possible shapes are a square or a circle. A square has a side length.
+A circle has a radius.
+
+A color has a blue component, a red component and a green component.
+Each component of a color is an integer value (between 0 and 255).
+
+->
+
+~~~
+case class Image(shape: Shape, strokeWidth: Int, color: Color)
+
+enum Shape {
+  case Square(side: Double)
+  case Circle(radius: Double)
+}
+
+case class Color(red: Int, green: Int, blue: Int)
+~~~
+
+Example: Painting Application
+=============================
+
+~~~
+case class Image(shape: Shape, strokeWidth: Int, color: Color)
+
+enum Shape
+  case Square(side: Double)
+  case Circle(radius: Double)
+
+case class Color(red: Int, green: Int, blue: Int)
+~~~
+
+A blue circle:
+
+~~~
+val blue = Color(0, 0, 255)
+val blueCircle = Image(Circle(10), 1, blue)
+~~~
+
+Data Types Define a Space of Possible States
+============================================
+
+The domain model defines the rules for constructing
+objects representing a possible state of a program.
+
+The more possible states, the more risks to have bugs
+or to reach illegal states.
+
+Hint: restrict the space of possible states to the minimum.
+
+Summary
+=======
+
+In this lecture we have seen how to model the business domain
+of a program using enumerations and case classes.
diff --git a/lectures/progfun1-3-1.md b/lectures/progfun1-3-1.md
new file mode 100644
index 0000000000000000000000000000000000000000..09383cbf32a963dec7ee24debf1e1024f33947f8
--- /dev/null
+++ b/lectures/progfun1-3-1.md
@@ -0,0 +1,199 @@
+% More Fun with Rationals
+%
+%
+
+More Examples With Rationals
+============================
+
+\begin{tabular}{ll}
+ \verb@val half = Rational(1, 2)@  \wsf half: Rational = Rational(1, 2) \\
+ \verb@half.add(half)@             \wsf Rational(3, 3)
+\end{tabular}
+
+Data Abstraction
+================
+
+The previous example has shown that rational numbers aren't always
+represented in their simplest form. (Why?)
+
+One would expect the rational numbers to be _simplified_:
+
+- reduce them to their smallest numerator and denominator by dividing both with a divisor.
+
+We could implement this in each rational operation, but it would be easy
+to forget this division in an operation.
+
+A better alternative consists of simplifying the representation in
+the class when the objects are constructed:
+
+Rationals with Data Abstraction
+===============================
+
+      class Rational(x: Int, y: Int)
+        private def gcd(a: Int, b: Int): Int = if b == 0 then a else gcd(b, a % b)
+        private val g = gcd(x, y)
+        def numer = x / g
+        def denom = y / g
+        ...
+
+We turned the `case class` definition into a simple `class` definition.
+
+A class definition also introduces a type (named `Rational`) and
+a constructor, but there are some differences with case classes:
+
+- the `x` and `y` constructor parameters are not available as
+  object fields
+
+`gcd` and `g` are \red{private} members; we can only access them
+from inside the `Rational` class.
+
+In this example, we calculate `gcd` immediately, so that its value can be re-used
+in the calculations of `numer` and `denom`.
+
+Rationals with Data Abstraction (2)
+===================================
+
+It is also possible to call `gcd` in the code of
+`numer` and `denom`:
+
+      class Rational(x: Int, y: Int)
+        private def gcd(a: Int, b: Int): Int = if b == 0 then a else gcd(b, a % b)
+        def numer = x / gcd(x, y)
+        def denom = y / gcd(x, y)
+
+This can be advantageous if it is expected that the functions `numer`
+and `denom` are called infrequently.
+
+Rationals with Data Abstraction (3)
+===================================
+
+It is equally possible to turn `numer` and `denom` into `val`s, so that they are computed only once:
+
+      class Rational(x: Int, y: Int)
+        private def gcd(a: Int, b: Int): Int = if b == 0 then a else gcd(b, a % b)
+        val numer = x / gcd(x, y)
+        val denom = y / gcd(x, y)
+
+This can be advantageous if the functions `numer` and `denom` are called often.
+
+The Client's View
+=================
+
+Clients observe exactly the same behavior in each case.
+
+This ability to choose different implementations of the data without affecting
+clients is called \red{data abstraction}.
+
+It is a cornerstone of software engineering.
+
+Self Reference
+==============
+
+On the inside of a class, the name `this` represents the object
+on which the current method is executed.
+
+\example
+
+Add the functions `less` and `max` to the class `Rational`.
+
+      class Rational(x: Int, y: Int)
+        ...
+        def less(that: Rational) =
+          numer * that.denom < that.numer * denom
+
+        def max(that: Rational) =
+          if this.less(that) that else this
+
+Self Reference (2)
+==================
+
+Note that a simple name `x`, which refers to another member
+of the class, is an abbreviation of `this.x`. Thus, an equivalent
+way to formulate `less` is as follows.
+
+      def less(that: Rational) =
+        this.numer * that.denom < that.numer * this.denom
+
+Preconditions
+=============
+
+Let's say our `Rational` class requires that the denominator is positive.
+
+We can enforce this by calling the `require` function.
+
+      class Rational(x: Int, y: Int)
+        require(y > 0, "denominator must be positive")
+        ...
+
+`require` is a predefined function.
+
+It takes a condition and an optional message string.
+
+If the condition passed to `require` is `false`, an `IllegalArgumentException` is thrown with the given message string.
+
+Assertions
+==========
+
+Besides `require`, there is also `assert`.
+
+Assert also takes a condition and an optional message string as parameters. E.g.
+
+      val x = sqrt(y)
+      assert(x >= 0)
+
+Like `require`, a failing `assert` will also throw an exception, but it's a different one: `AssertionError` for `assert`, `IllegalArgumentException` for `require`.
+
+This reflects a difference in intent
+
+ - `require` is used to enforce a precondition on the caller of a function.
+ - `assert` is used as to check the code of the function itself.
+
+Constructors
+============
+
+In Scala, a class implicitly introduces a constructor. This one
+is called the \red{primary constructor} of the class.
+
+The primary constructor
+
+ - takes the parameters of the class
+ - and executes all statements in the class body
+   (such as the `require` a couple of slides back).
+
+Auxiliary Constructors
+======================
+
+Scala also allows the declaration of \red{auxiliary constructors}.
+
+These are methods named `this`
+
+\example Adding an auxiliary constructor to the class `Rational`.
+
+      class Rational(x: Int, y: Int)
+        def this(x: Int) = this(x, 1)
+        ...
+
+\begin{worksheet}
+      Rational(2)   \wsf   2/1
+\end{worksheet}
+
+
+Exercise
+========
+
+Modify the `Rational` class so that rational numbers are kept unsimplified internally,
+but the simplification is applied when numbers are converted to strings.
+
+Do clients observe the same behavior when interacting with the rational class? \bigskip
+
+\begin{tabular}{lp{8cm}}
+   \verb@      O      @ & yes
+\\ \verb@      O      @ & no
+\\ \verb@      O      @ & yes for small sizes of denominators and nominators and small numbers of operations.
+\end{tabular}
+
+
+
+
+
+
diff --git a/lectures/progfun1-3-2.md b/lectures/progfun1-3-2.md
new file mode 100644
index 0000000000000000000000000000000000000000..052bad82a37359f3416a5418930642707b5145a1
--- /dev/null
+++ b/lectures/progfun1-3-2.md
@@ -0,0 +1,185 @@
+% Evaluation and Operators
+%
+%
+
+Classes and Substitutions
+=========================
+
+We previously defined the meaning of a function application using
+a computation model based on substitution. Now we extend this
+model to classes and objects.
+
+\red{Question:} How is an instantiation of the class
+$\btt new\ C(e_1, ..., e_m)$ evaluted?
+
+\red{Answer:} The expression arguments $\btt e_1, ..., e_m$
+are evaluated like the arguments of a normal function. That's it.
+
+The resulting expression, say, $\btt new\ C(v_1, ..., v_m)$, is
+already a value.
+
+Classes and Substitutions
+=========================
+
+Now suppose that we have a class definition,
+
+$$\btt
+      class\ C(x_1, ..., x_m) \{\ ...\ def\ f(y_1, ..., y_n) = b\ ...\ \}
+$$
+where
+
+ - The formal parameters of the class are $\btt x_1, ..., x_m$.
+ - The class defines a method $\btt f$ with formal parameters
+   $\btt y_1, ..., y_n$.
+
+(The list of function parameters can be absent. For simplicity, we
+have omitted the parameter types.)
+
+\red{Question:} How is the following expression evaluated?
+$$\btt
+        new\ C(v_1, ..., v_m).f(w_1, ..., w_n)
+$$
+
+Classes and Substitutions (2)
+=============================
+
+\red{Answer:} The expression $\btt new\ C(v_1, ..., v_m).f(w_1, ..., w_n)$ is rewritten to:
+$$\btt
+[w_1/y_1, ..., w_n/y_n]
+  [v_1/x_1, ..., v_m/x_m]
+    [C(v_1, ..., v_m)/this]\,b
+$$
+There are three substitutions at work here:
+
+ - the substitution of the formal parameters $\btt y_1, ..., y_n$ of the function $\btt f$ by the
+arguments $\btt w_1, ..., w_n$,
+ - the substitution of the formal parameters $\btt x_1, ..., x_m$ of the class $\btt C$ by the class arguments
+$\btt v_1, ..., v_m$,
+ - the substitution of the self reference $this$ by the value of the object $\btt C(v_1, ..., v_n)$.
+
+Object Rewriting Examples
+=========================
+
+   `      Rational(1, 2).numer`
+->
+   $\rightarrow$  $\btt [1/x, 2/y]\ []\ [Rational(1, 2)/this]$ `x`
+->
+   =$\,$  `1`
+\medskip
+->
+  `      Rational(1, 2).less(Rational(2, 3))`
+->
+   $\rightarrow$ $\btt [1/x, 2/y]\ [Rational(2, 3)/that]\ [new\ Rational(1, 2)/this]$
+
+\nl\gap `this.numer * that.denom < that.numer * this.denom`
+->
+   =$\,$  `Rational(1, 2).numer * Rational(2, 3).denom <`
+
+\nl\gap `Rational(2, 3).numer * Rational(1, 2).denom`
+->
+   $\rightarrow\dhd$  `1 * 3 < 2 * 2`
+
+   $\rightarrow\dhd$  `true`
+
+
+Operators
+=========
+
+In principle, the rational numbers defined by `Rational` are
+as natural as integers.
+
+But for the user of these abstractions, there is a noticeable
+difference:
+
+ - We write `x + y`, if `x` and `y` are integers, but
+ - We write `r.add(s)` if `r` and `s` are rational numbers.
+
+In Scala, we can eliminate this difference. We proceed in two steps.
+
+Step 1: Infix Notation
+======================
+
+Any method with a parameter can be used like an infix operator.
+
+It is therefore possible to write
+
+      r add s                                         r.add(s)
+      r less s          /* in place of */             r.less(s)
+      r max s                                         r.max(s)
+
+Step 2: Relaxed Identifiers
+===========================
+
+Operators can be used as identifiers.
+
+Thus, an identifier can be:
+
+ - \red{Alphanumeric}: starting with a letter, followed by a sequence of letters or numbers
+ - \red{Symbolic}: starting with an operator symbol, followed by other operator symbols.
+ - The underscore character `'_'` counts as a letter.
+ - Alphanumeric identifiers can also end in an underscore, followed by some operator symbols.
+
+Examples of identifiers:
+
+      x1     *     +?%&     vector_++     counter_=
+
+
+
+
+
+Operators for Rationals
+=======================
+
+A more natural definition of class `Rational`:
+
+      class Rational(x: Int, y: Int) {
+        private def gcd(a: Int, b: Int): Int = if b == 0 then a else gcd(b, a % b)
+        private val g = gcd(x, y)
+        def numer = x / g
+        def denom = y / g
+        def + (r: Rational) =
+          Rational(
+            numer * r.denom + r.numer * denom,
+            denom * r.denom)
+        def - (r: Rational) = ...
+        def * (r: Rational) = ...
+        ...
+      }
+
+Operators for Rationals
+=======================
+
+... and rational numbers can be used like `Int` or `Double`:
+
+      val x = Rational(1, 2)
+      val y = Rational(1, 3)
+      x * x + y * y
+
+Precedence Rules
+================
+
+The \red{precedence} of an operator is determined by its first character.
+
+The following table lists the characters in increasing order of priority precedence:
+
+        (all letters)
+        |
+        ^
+        &
+        < >
+        = !
+        :
+        + -
+        * / %
+        (all other special characters)
+
+Exercise
+========
+
+Provide a fully parenthized version of
+
+     a + b ^? c ?^ d less a ==> b | c
+
+Every binary operation needs to be put into parentheses,
+but the structure of the expression should not change.
+
diff --git a/lectures/progfun1-3-3.md b/lectures/progfun1-3-3.md
new file mode 100644
index 0000000000000000000000000000000000000000..6ff97d64f298cc21b5666bae9e01c9586dfdbd21
--- /dev/null
+++ b/lectures/progfun1-3-3.md
@@ -0,0 +1,192 @@
+% Class Hierarchies
+%
+%
+
+
+Abstract Classes
+================
+
+Consider the task of writing a class for sets of integers with
+the following operations.
+
+      abstract class IntSet
+        def incl(x: Int): IntSet
+        def contains(x: Int): Boolean
+
+`IntSet` is an \red{abstract class}.
+
+Abstract classes can contain members which are
+missing an implementation (in our case, `incl` and `contains`).
+
+Consequently, no instances of an abstract class can be created with
+the operator \verb$new$.
+
+Class Extensions
+================
+
+Let's consider implementing sets as binary trees.
+
+There are two types of possible trees: a tree for the empty set, and
+a tree consisting of an integer and two sub-trees.
+
+Here are their implementations:
+
+      class Empty() extends IntSet
+        def contains(x: Int): Boolean = false
+        def incl(x: Int): IntSet = NonEmpty(x, Empty(), Empty())
+
+Class Extensions (2)
+====================
+
+      class NonEmpty(elem: Int, left: IntSet, right: IntSet) extends IntSet
+
+        def contains(x: Int): Boolean =
+          if x < elem then left.contains(x)
+          else if x > elem then right.contains(x)
+          else true
+
+        def incl(x: Int): IntSet =
+          if x < elem then NonEmpty(elem, left.incl(x), right)
+          else if x > elem then NonEmpty(elem, left, right.incl(x))
+          else this
+
+Terminology
+===========
+
+`Empty` and `NonEmpty` both \red{extend} the class `IntSet`.
+
+This implies that the types `Empty` and `NonEmpty` \red{conform} to the type `IntSet`
+
+- an object of type `Empty` or `NonEmpty` can be used wherever an object of type `IntSet` is required.
+
+Base Classes and Subclasses
+===========================
+
+`IntSet` is called the \red{superclass} of `Empty`
+and `NonEmpty`.
+
+`Empty` and `NonEmpty` are \red{subclasses} of
+`IntSet`.
+
+In Scala, any user-defined class extends another class.
+
+If no superclass is given, the standard class `Object` in the Java package `java.lang` is assumed.
+
+The direct or indirect superclasses of a class `C` are called \red{base classes} of `C`.
+
+So, the base classes of `NonEmpty` are `IntSet` and `Object`.
+
+
+Implementation and Overriding
+=============================
+
+The definitions of `contains` and `incl` in the classes
+`Empty` and `NonEmpty` \red{implement} the abstract
+functions in the base trait `IntSet`.
+
+It is also possible to \red{redefine} an existing, non-abstract
+definition in a subclass by using `override`.
+
+\example
+
+      abstract class Base                         class Sub extends Base
+        def foo = 1                                 override def foo = 2
+        def bar: Int                                def bar = 3
+
+Object Definitions
+==================
+
+In the `IntSet` example, one could argue that there is really only a single empty `IntSet`.
+
+So it seems overkill to have the user create many instances of it.
+
+We can express this case better with an _object definition_:
+
+      object Empty extends IntSet
+        def contains(x: Int): Boolean = false
+        def incl(x: Int): IntSet = NonEmpty(x, Empty, Empty)
+
+This defines a \red{singleton object} named `Empty`.
+
+No other `Empty` instances can be (or need to be) created.
+
+Singleton objects are values, so `Empty` evaluates to itself.
+
+Programs
+========
+
+So far we have executed all Scala code from the REPL or the worksheet.
+
+But it is also possible to create standalone applications in Scala.
+
+Each such application contains an object with a `main` method.
+
+For instance, here is the "Hello World!" program in Scala.
+
+      object Hello
+        def main(args: Array[String]) = println("hello world!")
+
+Once this program is compiled, you can start it from the command line with
+
+     > scala Hello
+
+Exercise
+========
+
+Write a method `union` for forming the union of two sets. You should
+implement the following abstract class.
+
+      abstract class IntSet
+        def incl(x: Int): IntSet
+        def contains(x: Int): Boolean
+        def union(other: IntSet): IntSet
+
+Dynamic Binding
+===============
+
+Object-oriented languages (including Scala) implement \red{dynamic method dispatch}.
+
+This means that the code invoked by a method call depends on the
+runtime type of the object that contains the method.
+
+\example
+
+   `Empty.contains(1)`
+->
+   $\rightarrow$  $\btt [1/x]\ [Empty/this]$ `false`
+->
+   =$\,$ `false`
+
+Dynamic Binding (2)
+===================
+
+Another evaluation using `NonEmpty`:
+
+   `(NonEmpty(7, Empty, Empty)).contains(7)`
+->
+   $\rightarrow$  $\btt [7/elem]\ [7/x]\ [new\ NonEmpty(7, Empty, Empty)/this]$
+
+\nl \quad$\ $ `if x < elem then this.left.contains(x)`
+
+\nl\gap$\ $ `else if x > elem then this.right.contains(x) else true`
+->
+  =$\,$  `if 7 < 7 then NonEmpty(7, Empty, Empty).left.contains(7)`
+
+\nl\gap `else if 7 > 7 then NonEmpty(7, Empty, Empty).right`
+
+\nl\gap\gap `.contains(7) else true`
+->
+   $\rightarrow$  `true`
+
+Something to Ponder
+===================
+
+Dynamic dispatch of methods is analogous to calls to
+higher-order functions.
+
+\red{Question:}
+
+Can we implement one concept in terms of the other?
+
+ - Objects in terms of higher-order functions?
+ - Higher-order functions in terms of objects?
\ No newline at end of file
diff --git a/lectures/progfun1-3-4.md b/lectures/progfun1-3-4.md
new file mode 100644
index 0000000000000000000000000000000000000000..d90c46acc81c9800bdc5c77c4a6deadf81b40ac8
--- /dev/null
+++ b/lectures/progfun1-3-4.md
@@ -0,0 +1,193 @@
+% How Classes are Organized
+%
+%
+
+Packages
+========
+
+Classes and objects are organized in packages.
+
+To place a class or object inside a package, use a package clause
+at the top of your source file.
+
+      package progfun.examples
+
+      object Hello { ... }
+
+This would place `Hello` in the package `progfun.examples`.
+
+You can then refer to `Hello` by its _fully qualified name_
+`progfun.examples.Hello`. For instance, to run the `Hello` program:
+
+       > scala progfun.examples.Hello
+
+Imports
+=======
+
+Say we have a class `Rational` in package `week3`.
+
+You can use the class using its fully qualified name:
+
+      val r = new week3.Rational(1, 2)
+
+Alternatively, you can use an import:
+
+      import week3.Rational
+      val r = new Rational(1, 2)
+
+Forms of Imports
+================
+
+Imports come in several forms:
+
+      import week3.Rational           // imports just Rational
+      import week3.{Rational, Hello}  // imports both Rational and Hello
+      import week3._                  // imports everything in package week3
+
+The first two forms are called _named imports_.
+
+The last form is called a _wildcard import_.
+
+You can import from either a package or an object.
+
+Automatic Imports
+=================
+
+Some entities are automatically imported in any Scala program.
+
+These are:
+
+ - All members of package `scala`
+ - All members of package `java.lang`
+ - All members of the singleton object `scala.Predef`.
+
+Here are the fully qualified names of some types and functions which you have seen so far:
+
+      Int                            scala.Int
+      Boolean                        scala.Boolean
+      Object                         java.lang.Object
+      require                        scala.Predef.require
+      assert                         scala.Predef.assert
+
+Scaladoc
+========
+
+You can explore the standard Scala library using the scaladoc web pages.
+
+You can start at
+
+ [\blue{www.scala-lang.org/api/current}](http://www.scala-lang.org/api/current)
+
+Traits
+======
+
+In Java, as well as in Scala, a class can only have one superclass.
+
+But what if a class has several natural supertypes to which it conforms
+or from which it wants to inherit code?
+
+Here, you could use `trait`s.
+
+A trait is declared like an abstract class, just with `trait` instead of
+`abstract class`.
+
+      trait Planar {
+        def height: Int
+        def width: Int
+        def surface = height * width
+      }
+
+Traits (2)
+==========
+
+Classes, objects and traits can inherit from at most one class but
+arbitrary many traits.
+
+Example:
+
+     class Square extends Shape with Planar with Movable ...
+
+Traits resemble interfaces in Java, but are more powerful because they can contains fields and concrete methods.
+
+On the other hand, traits cannot have (value) parameters, only classes can.
+
+
+Scala's Class Hierarchy
+=======================
+
+\includegraphics[scale=0.33]{images/classhierarchy.pdf}
+
+Top Types
+=========
+
+At the top of the type hierarchy we find:
+
+\begin{tabular}{ll}
+\verb@Any           @ & the base type of all types
+\\[1em]
+                 & Methods: `==`, `!=`, `equals`, `hashCode, `toString`
+\\[2em]
+\verb@AnyRef@    & The base type of all reference types;
+\\               & Alias of `java.lang.Object`
+\\[2em]
+\verb@AnyVal@     & The base type of all primitive types.
+\end{tabular}
+
+The Nothing Type
+================
+
+`Nothing` is at the bottom of Scala's type hierarchy. It is a subtype of every other type.
+
+There is no value of type `Nothing`.
+
+Why is that useful?
+
+ - To signal abnormal termination
+ - As an element type of empty collections (see next session)
+
+Exceptions
+==========
+
+Scala's exception handling is similar to Java's.
+
+The expression
+
+     throw Exc
+
+aborts evaluation with the exception `Exc`.
+
+The type of this expression is `Nothing`.
+
+The Null Type
+=============
+
+Every reference class type also has `null` as a value.
+
+The type of `null` is `Null`.
+
+`Null` is a subtype of every class that inherits from `Object`; it is
+incompatible with subtypes of `AnyVal`.
+
+     val x = null         // x: Null
+     val y: String = null // y: String
+     val z: Int = null    // error: type mismatch
+
+
+
+Exercise
+========
+
+What is the type of
+
+      if true then 1 else false
+
+      O        Int
+      O        Boolean
+      O        AnyVal
+      O        Object
+      O        Any
+
+
+
+
+
diff --git a/lectures/progfun1-3-5.md b/lectures/progfun1-3-5.md
new file mode 100644
index 0000000000000000000000000000000000000000..88c6ccc12d311b59b4c8e3dfc1e2e4042b438526
--- /dev/null
+++ b/lectures/progfun1-3-5.md
@@ -0,0 +1,128 @@
+% Objects Everywhere
+%
+%
+
+Pure Object Orientation
+=======================
+
+A pure object-oriented language is one in which every  value is an
+object.
+
+If the language is based on classes, this means that the type of
+each value is a class.
+
+Is Scala a pure object-oriented language?
+
+At first glance, there seem to be some exceptions: primitive types,
+functions.
+
+But, let's look closer:
+
+Standard Classes
+================
+
+Conceptually, types such as `Int` or `Boolean` do not
+receive special treatment in Scala. They are like the other classes,
+defined in the package `scala`.
+
+For reasons of efficiency, the Scala compiler represents
+the values of type `scala.Int` by 32-bit integers, and
+the values of type `scala.Boolean` by Java's Booleans, etc.
+
+Pure Booleans
+=============
+
+The `Boolean` type maps to the JVM's primitive type `boolean`.
+
+But one _could_ define it as a class from first principles:
+
+      package idealized.scala
+      abstract class Boolean extends AnyVal
+        def ifThenElse[T](t: => T, e: => T): T
+
+        def && (x: => Boolean): Boolean = ifThenElse(x, false)
+        def || (x: => Boolean): Boolean = ifThenElse(true, x)
+        def unary_!: Boolean            = ifThenElse(false, true)
+
+        def == (x: Boolean): Boolean    = ifThenElse(x, x.unary_!)
+        def != (x: Boolean): Boolean    = ifThenElse(x.unary_!, x)
+        ...
+
+Boolean Constants
+=================
+
+Here are constants `true` and `false` that go with `Boolean` in `idealized.scala`:
+
+      package idealized.scala
+
+      object true extends Boolean
+        def ifThenElse[T](t: => T, e: => T) = t
+
+      object false extends Boolean
+        def ifThenElse[T](t: => T, e: => T) = e
+
+Exercise
+========
+
+Provide an implementation of the comparison operator `<`
+in class `idealized.scala.Boolean`.
+
+Assume for this that `false` < `true`.
+->
+
+
+The class Int
+=============
+
+Here is a partial specification of the class `scala.Int`.
+
+      class Int
+        def + (that: Double): Double
+        def + (that: Float): Float
+        def + (that: Long): Long
+        def + (that: Int): Int         // same for -, *, /, %
+
+        def << (cnt: Int): Int         // same for >>, >>>  */
+
+        def & (that: Long): Long
+        def & (that: Int): Int         // same for |, ^ */
+
+The class Int (2)
+=================
+
+
+        def == (that: Double): Boolean
+        def == (that: Float): Boolean
+        def == (that: Long): Boolean   // same for !=, <, >, <=, >=
+        ...
+
+Can it be represented as a class from first principles (i.e. not using primitive `int`s?
+
+Exercise
+========
+
+Provide an implementation of the abstract class `Nat` that represents non-negative integers.
+
+
+      abstract class Nat
+        def isZero: Boolean
+        def predecessor: Nat
+        def successor: Nat
+        def + (that: Nat): Nat
+        def - (that: Nat): Nat
+
+Exercise (2)
+============
+
+Do not use standard numerical classes in this implementation.
+
+Rather, implement a sub-object and a sub-class:
+
+      object Zero extends Nat
+      class Succ(n: Nat) extends Nat
+
+One for the number zero, the other for strictly positive numbers.
+
+(this one is a bit more involved than previous quizzes).
+
+\quiz
\ No newline at end of file
diff --git a/lectures/progfun1-4-1.md b/lectures/progfun1-4-1.md
new file mode 100644
index 0000000000000000000000000000000000000000..7ab595d440263d27b6260a75394b588bb9276549
--- /dev/null
+++ b/lectures/progfun1-4-1.md
@@ -0,0 +1,165 @@
+% Polymorphism
+%
+%
+
+Cons-Lists
+==========
+
+A fundamental data structure in many functional languages is
+the immutable linked list.
+
+It is constructed from two building blocks:
+
+\begin{tabular}{ll}
+ \verb@Nil@       & the empty list \\
+ \verb@Cons@      & a cell containing an element and the remainder of the list.
+\end{tabular}
+
+Examples for Cons-Lists
+=======================
+
+
+
+     List(1, 2, 3)
+
+
+
+
+
+
+
+     List(List(true, false), List(3))
+
+Cons-Lists in Scala
+===================
+
+Here's an outline of a class hierarchy that represents lists of integers in this fashion:
+
+      package week4
+
+      trait IntList ...
+      class Cons(val head: Int, val tail: IntList) extends IntList ...
+      class Nil() extends IntList ...
+
+A list is either
+
+ - an empty list `Nil()`, or
+ - a list `Cons(x, xs)` consisting of a `head` element `x`
+   and a `tail` list `xs`.
+
+
+Value Parameters
+================
+
+Note the abbreviation `(val head: Int, val tail: IntList)` in the definition of `Cons`.
+
+This defines at the same time parameters and fields of a class.
+
+It is equivalent to:
+
+      class Cons(_head: Int, _tail: IntList) extends IntList
+        val head = _head
+        val tail = _tail
+
+where `_head` and `_tail` are otherwise unused names.
+
+Type Parameters
+===============
+
+It seems too narrow to define only lists with `Int` elements.
+
+We'd need another class hierarchy for `Double` lists, and so on, one for each possible element type.
+
+We can generalize the definition using a type parameter:
+
+      package week4
+
+      trait List[T]
+      class Cons[T](val head: T, val tail: List[T]) extends List[T]
+      class Nil[T]() extends List[T]
+
+Type parameters are written in square brackets, e.g. `[T]`.
+
+
+Complete Definition of List
+===========================
+
+      trait List[T]
+        def isEmpty: Boolean
+        def head: T
+        def tail: List[T]
+
+      class Cons[T](val head: T, val tail: List[T]) extends List[T]
+        def isEmpty = false
+
+      class Nil[T] extends List[T]
+        def isEmpty = true
+        def head = throw NoSuchElementException("Nil.head")
+        def tail = throw NoSuchElementException("Nil.tail")
+
+Generic Functions
+=================
+
+Like classes, functions can have type parameters.
+
+For instance, here is a function that creates a list consisting of a single element.
+
+      def singleton[T](elem: T) = Cons[T](elem, Nil[T]())
+
+We can then write:
+
+      singleton[Int](1)
+      singleton[Boolean](true)
+
+Type Inference
+==============
+
+In fact, the Scala compiler can usually deduce the correct type
+parameters from the value arguments of a function call.
+
+So, in most cases, type parameters can be left out. You could also write:
+
+      singleton(1)
+      singleton(true)
+
+Types and Evaluation
+====================
+
+Type parameters do not affect evaluation in Scala.
+
+We can assume that all type parameters and type arguments are removed
+before evaluating the program.
+
+This is also called \red{type erasure}.
+
+Languages that use type erasure include Java, Scala, Haskell, ML, OCaml.
+
+Some other languages keep the type parameters around at run time, these include C++, C#, F#.
+
+Polymorphism
+============
+
+Polymorphism means that a function type comes "in many forms".
+
+In programming it means that
+
+ - the function can be applied to arguments of many types, or
+ - the type can have instances of many types.
+->
+
+We have seen two principal forms of polymorphism:
+
+ - subtyping: instances of a subclass can be passed to a base class
+ - generics: instances of a function or class are created by type parameterization.
+
+Exercise
+========
+
+Write a function `nth` that takes an integer `n` and a list and
+selects the `n`'th element of the list.
+
+Elements are numbered from 0.
+
+If index is outside the range from `0` up the the length of the list minus one, a `IndexOutOfBoundsException` should be thrown.
+
+\quiz
\ No newline at end of file
diff --git a/lectures/progfun1-4-2.md b/lectures/progfun1-4-2.md
new file mode 100644
index 0000000000000000000000000000000000000000..8cbcbb004aa41d68aa767ef23ced74fb5d5a970f
--- /dev/null
+++ b/lectures/progfun1-4-2.md
@@ -0,0 +1,174 @@
+% Subtyping and Generics
+%
+%
+
+Polymorphism
+============
+
+Two principal forms of polymorphism:
+
+ - subtyping
+ - generics
+
+In this session we will look at their interactions.
+
+Two main areas:
+
+ - bounds
+ - variance
+
+Type Bounds
+===========
+
+Consider the method `assertAllPos` which
+
+ - takes an `IntSet`
+ - returns the `IntSet` itself if all its elements are positive
+ - throws an exception otherwise
+
+What would be the best type you can give to `assertAllPos`? Maybe:
+->
+      def assertAllPos(s: IntSet): IntSet
+
+In most situations this is fine, but can one be more precise?
+
+Type Bounds
+===========
+
+One might want to express that `assertAllPos`
+takes `Empty` sets to `Empty` sets and `NonEmpty` sets to `NonEmpty` sets.
+
+A way to express this is:
+
+     def assertAllPos[S <: IntSet](r: S): S = ...
+
+Here, "`<: IntSet`" is an \red{upper bound} of the type parameter `S`:
+
+It means that `S` can be instantiated only to types that conform to `IntSet`.
+
+Generally, the notation
+
+  - `S <: T` means: _`S` is a subtype of `T`_, and
+  - `S >: T` means: _`S` is a supertype of `T`_, or _`T` is a subtype of `S`_.
+
+Lower Bounds
+============
+
+You can also use a lower bound for a type variable.
+
+\example
+
+       [S >: NonEmpty]
+
+introduces a type parameter `S` that can range only over \red{supertypes}
+of `NonEmpty`.
+
+So `S` could be one of `NonEmpty`, `IntSet`, `AnyRef`, or `Any`.
+
+We will see later on in this session where lower bounds are useful.
+
+Mixed Bounds
+============
+
+Finally, it is also possible to mix a lower bound with an upper bound.
+
+For instance,
+
+        [S >: NonEmpty <: IntSet]
+
+would restrict `S` any type on the interval between `NonEmpty` and `IntSet`.
+
+Covariance
+==========
+
+There's another interaction between subtyping and type parameters we
+need to consider. Given:
+
+       NonEmpty <: IntSet
+
+is
+
+       List[NonEmpty] <: List[IntSet]    ?
+->
+Intuitively, this makes sense: A list of non-empty sets is a special case of a list of arbitrary sets.
+
+We call types for which this relationship holds \red{covariant}
+because their subtyping relationship varies with the type parameter.
+
+Does covariance make sense for all types, not just for `List`?
+
+Arrays
+======
+
+For perspective, let's look at arrays in Java (and C#).
+
+Reminder:
+
+ - An array of `T` elements is written `T[]` in Java.
+ - In Scala we use parameterized type syntax `Array[T]` to refer to the same type.
+
+Arrays in Java are covariant, so one would have:
+
+        NonEmpty[] <: IntSet[]
+
+Array Typing Problem
+====================
+
+But covariant array typing causes problems.
+
+To see why, consider the Java code below.
+
+\begin{lstlisting}
+  NonEmpty[] a = new NonEmpty[]{new NonEmpty(1, new Empty(), new Empty())}
+  IntSet[] b = a
+  b[0] = new Empty()
+  NonEmpty s = a[0]
+\end{lstlisting}
+
+It looks like we assigned in the last line an `Empty` set to a
+variable of type `NonEmpty`!
+
+What went wrong?
+
+The Liskov Substitution Principle
+================================
+
+The following principle, stated by Barbara Liskov, tells us when a
+type can be a subtype of another.
+
+\begin{quote}
+If \verb`A <: B`,
+then everything one can to do with a value of type \verb`B` one should also
+be able to do with a value of type \verb`A`.
+\end{quote}
+
+[The actual definition Liskov used is a bit more formal. It says:
+
+\begin{quote}
+Let \verb`q(x)` be a property provable about objects \verb`x` of type \verb`B`.
+Then \verb`q(y)` should be provable for objects \verb`y` of type \verb`A` where \verb`A <: B`.
+\end{quote}
+]
+
+Exercise
+========
+
+The problematic array example would be written as follows in Scala:
+
+      val a: Array[NonEmpty] = Array(NonEmpty(1, Empty(), Empty()))
+      val b: Array[IntSet] = a
+      b(0) = Empty()
+      val s: NonEmpty = a(0)
+
+When you try out this example, what do you observe?
+
+\begin{tabular}{ll}
+   \verb`  O         ` &       A type error in line 1
+\\ \verb`  O         ` &       A type error in line 2
+\\ \verb`  O         ` &       A type error in line 3
+\\ \verb`  O         ` &       A type error in line 4
+\\ \verb`  O         ` &       A program that compiles and throws an exception at run-time
+\\ \verb`  O         ` &       A program that compiles and runs without exception
+\end{tabular}
+->
+\quiz
\ No newline at end of file
diff --git a/lectures/progfun1-4-3.md b/lectures/progfun1-4-3.md
new file mode 100644
index 0000000000000000000000000000000000000000..ba76748076844ecc0b47332b77685397dfb22360
--- /dev/null
+++ b/lectures/progfun1-4-3.md
@@ -0,0 +1,236 @@
+% Variance
+%
+%
+
+Variance
+========
+
+You have seen the the previous session that some types should be covariant whereas
+others should not.
+
+Roughly speaking, a type that accepts mutations of its elements should
+not be covariant.
+
+But immutable types can be covariant, if some conditions
+on methods are met.
+
+Definition of Variance
+======================
+
+Say `C[T]` is a parameterized type and `A`, `B` are types such that `A <: B`.
+
+In general, there are _three_ possible relationships between `C[A]` and `C[B]`:
+
+\begin{tabular}{p{8cm}l}
+    \verb@C[A] <: C[B]@  & \verb@C@ is \red{covariant}
+\\  \verb@C[A] >: C[B]@  & \verb@C@ is \red{contravariant}
+\\  neither \verb@C[A]@ nor \verb@C[B]@ is a subtype of the other & \verb@C@ is \red{nonvariant}
+\end{tabular}\medskip
+
+->
+
+Scala lets you declare the variance of a type by annotating the type parameter:
+
+\begin{tabular}{p{8cm}l}
+    \verb@class C[+A] { ... }@  & \verb@C@ is \red{covariant}
+\\  \verb@class C[-A] { ... }@  & \verb@C@ is \red{contravariant}
+\\  \verb@class C[A] { ... }         @ & \verb@C@ is \red{nonvariant}
+\end{tabular}
+
+Exercise
+========
+
+Say you have two function types:
+
+       type A = IntSet => NonEmpty
+       type B = NonEmpty => IntSet
+
+According to the Liskov Substitution Principle, which of the
+following should be true?
+
+     O         A <: B
+     O         B <: A
+     O         A and B are unrelated.
+->
+\quiz
+
+Typing Rules for Functions
+==========================
+
+Generally, we have the following rule for subtyping between function types:
+
+If `A2 <: A1` and `B1 <: B2`, then
+
+      A1 => B1  <:  A2 => B2
+
+Function Trait Declaration
+==========================
+
+So functions are _contravariant_ in their argument type(s) and
+_covariant_ in their result type.
+
+This leads to the following revised definition of the `Function1` trait:
+
+      package scala
+      trait Function1[-T, +U]
+        def apply(x: T): U
+
+Variance Checks
+===============
+
+We have seen in the array example that the combination of covariance with
+certain operations is unsound.
+
+In this case the problematic operation was the update operation on an array.
+
+If we turn `Array` into a class, and `update` into a method, it would look like this:
+
+      class Array[+T]
+        def update(x: T) = ...
+
+The problematic combination is
+
+ - the covariant type parameter `T`
+ - which appears in parameter position of the method `update`.
+
+Variance Checks (2)
+===================
+
+The Scala compiler will check that there are no problematic combinations when compiling a class with variance annotations.
+
+Roughly,
+
+ - _covariant_ type parameters can only appear in method results.
+ - _contravariant_ type parameters can only appear in method parameters.
+ - _invariant_ type parameters can appear anywhere.
+
+The precise rules are a bit more involved, fortunately the Scala compiler performs them for us.
+
+Variance-Checking the Function Trait
+====================================
+
+Let's have a look again at Function1:
+
+      trait Function1[-T, +U]
+        def apply(x: T): U
+
+Here,
+
+ - `T` is contravariant and appears only as a method parameter type
+ - `U` is covariant and appears only as a method result type
+
+So the method is checks out OK.
+
+Variance and Lists
+==================
+
+Let's get back to the previous implementation of lists.
+
+One shortcoming was that `Nil` had to be a class, whereas we would
+prefer it to be an object (after all, there is only one empty list).
+
+Can we change that?
+
+Yes, because we can make `List` covariant.
+->
+Here are the essential modifications:
+
+      trait List[+T]
+        ...
+      object Empty extends List[Nothing]
+        ...
+
+Making Classes Covariant
+========================
+
+Sometimes, we have to put in a bit of work to make a class covariant.
+
+Consider adding a `prepend` method to `List` which prepends a given
+element, yielding a new list.
+
+A first implementation of `prepend` could look like this:
+
+      trait List[+T]
+        def prepend(elem: T): List[T] = Cons(elem, this)
+
+But that does not work!
+
+Exercise
+========
+
+Why does the following code not type-check?
+
+      trait List[+T]
+        def prepend(elem: T): List[T] = Cons(elem, this)
+
+Possible answers:
+
+  `O` \tab    `prepend` turns `List` into a mutable class.
+
+  `O` \tab    `prepend` fails variance checking.
+
+  `O` \tab    `prepend`'s right-hand side contains a type error.
+->
+
+\quiz
+
+Prepend Violates LSP
+====================
+
+Indeed, the compiler is right to throw out `List` with `prepend`,
+because it violates the Liskov Substitution Principle:
+
+Here's something one can do with a list `xs` of type `List[IntSet]`:
+
+      xs.prepend(Empty)
+
+But the same operation on a list `ys` of type `List[NonEmpty]` would lead to a type error:
+
+      ys.prepend(Empty)
+                 ^ type mismatch
+                 required: NonEmpty
+                 found: Empty
+
+So, `List[NonEmpty]` cannot be a subtype of `List[IntSet]`.
+
+Lower Bounds
+============
+
+But `prepend` is a natural method to have on immutable lists!
+
+\question: How can we make it variance-correct?
+\medskip
+
+->
+We can use a _lower bound_:
+
+      def prepend [U >: T] (elem: U): List[U] = Cons(elem, this)
+
+This passes variance checks, because:
+
+ - _covariant_ type parameters may appear in _lower bounds_ of method type parameters
+ - _contravariant_ type parameters may appear in _upper bounds_.
+
+Exercise
+========
+
+Assume `prepend` in trait `List` is implemented like this:
+
+      def prepend [U >: T] (elem: U): List[U] = Cons(elem, this)
+
+What is the result type of this function:
+
+      def f(xs: List[NonEmpty], x: Empty) = xs.prepend(x)   ?
+\medskip
+
+Possible answers:
+
+\begin{tabular}{ll}
+   \verb`  O         ` &       does not type check
+\\ \verb`  O         ` &       \verb`List[NonEmpty]`
+\\ \verb`  O         ` &       \verb`List[Empty]`
+\\ \verb`  O         ` &       \verb`List[IntSet]`
+\\ \verb`  O         ` &       \verb`List[Any]`
+\end{tabular}
+->
+\quiz
\ No newline at end of file
diff --git a/lectures/progfun1-4-4.md b/lectures/progfun1-4-4.md
new file mode 100644
index 0000000000000000000000000000000000000000..945394cfd5df47f1e015f5174423a0b88cdb7490
--- /dev/null
+++ b/lectures/progfun1-4-4.md
@@ -0,0 +1,103 @@
+% Functions as Objects
+%
+%
+
+Functions as Objects
+====================
+
+We have seen that Scala's numeric types and the `Boolean`
+type can be implemented like normal classes.
+
+But what about functions?
+->
+In fact function values _are_ treated as objects in Scala.
+
+The function type `A => B` is just an abbreviation for the class
+`scala.Function1[A, B]`, which is defined as follows.
+
+      package scala
+      trait Function1[A, B]
+        def apply(x: A): B
+
+So functions are objects with `apply` methods.
+
+There are also traits `Function2`, `Function3`, ... for functions which take more parameters.
+
+
+Expansion of Function Values
+============================
+
+An anonymous function such as
+
+       (x: Int) => x * x
+
+is expanded to:
+->
+       { class AnonFun() extends Function1[Int, Int]
+           def apply(x: Int) = x * x
+         AnonFun()
+       }
+->
+or, shorter, using _anonymous class syntax_:
+
+       new Function1[Int, Int] {
+         def apply(x: Int) = x * x
+       }
+
+
+Expansion of Function Calls
+===========================
+
+A function call, such as `f(a, b)`, where `f` is a value of some class
+type, is expanded to
+
+      f.apply(a, b)
+
+So the OO-translation of
+
+      val f = (x: Int) => x * x
+      f(7)
+
+would be
+
+      val f = new Function1[Int, Int] {
+        def apply(x: Int) = x * x
+      }
+      f.apply(7)
+
+Functions and Methods
+=====================
+
+Note that a method such as
+
+       def f(x: Int): Boolean = ...
+
+is not itself a function value.
+
+But if `f` is used in a place where a Function type is expected, it is
+converted automatically to the function value
+
+       (x: Int) => f(x)
+
+or, expanded:
+
+       new Function1[Int, Boolean] {
+         def apply(x: Int) = f(x)
+      }
+
+Exercise
+========
+
+In package `week4`, define an
+
+      object List {
+        ...
+      }
+
+with 3 functions in it so that users can create lists of lengths 0-2 using syntax
+
+      List()        // the empty list
+      List(1)       // the list with single element 1
+      List(2, 3)    // the list with elements 2 and 3.
+
+\quiz
\ No newline at end of file
diff --git a/lectures/progfun1-4-5-decomposition.md b/lectures/progfun1-4-5-decomposition.md
new file mode 100644
index 0000000000000000000000000000000000000000..5586855d429416753b23de05b405e8525fd3cdd0
--- /dev/null
+++ b/lectures/progfun1-4-5-decomposition.md
@@ -0,0 +1,166 @@
+% Decomposition
+%
+%
+
+Decomposition
+=============
+
+Suppose you want to write a small interpreter for arithmetic
+expressions.
+
+To keep it simple, let's restrict ourselves to numbers and
+additions.
+
+Expressions can be represented as a class hierarchy, with a base trait
+`Expr` and two subclasses, `Number` and `Sum`.
+
+To treat an expression, it's necessary to know the expression's shape
+and its components.
+
+This brings us to the following implementation.
+
+Expressions
+===========
+
+      trait Expr
+        def isNumber: Boolean
+        def isSum: Boolean
+        def numValue: Int
+        def leftOp: Expr
+        def rightOp: Expr
+
+      class Number(n: Int) extends Expr
+        def isNumber: Boolean = true
+        def isSum: Boolean = false
+        def numValue: Int = n
+        def leftOp: Expr = throw Error("Number.leftOp")
+        def rightOp: Expr = throw Error("Number.rightOp")
+
+Expressions (2)
+===============
+
+      class Sum(e1: Expr, e2: Expr) extends Expr
+         def isNumber: Boolean = false
+         def isSum: Boolean = true
+         def numValue: Int = throw Error("Sum.numValue")
+         def leftOp: Expr = e1
+         def rightOp: Expr = e2
+
+Evaluation of Expressions
+=========================
+
+You can now write an evaluation function as follows.
+
+      def eval(e: Expr): Int =
+        if e.isNumber then e.numValue
+        else if e.isSum then eval(e.leftOp) + eval(e.rightOp)
+        else throw Error("Unknown expression " + e)
+
+\red{Problem}: Writing all these classification and accessor functions
+quickly becomes tedious!
+
+Adding New Forms of Expressions
+===============================
+
+So, what happens if you want to add new expression forms, say
+
+      class Prod(e1: Expr, e2: Expr) extends Expr   // e1 * e2
+      class Var(x: String) extends Expr             // Variable `x'
+
+You need to add methods for classification and access to all classes
+defined above.
+
+Question
+========
+
+To integrate `Prod` and `Var` into the hierarchy, how many new
+method definitions do you need?
+
+(including method definitions in `Prod` and `Var` themselves, but not counting methods that were already given on the slides)
+
+Possible Answers
+
+     O            9
+     O           10
+     O           19
+     O           25
+     O           35
+     O           40
+
+->
+\quiz
+
+Non-Solution: Type Tests and Type Casts
+=======================================
+
+A "hacky" solution could use type tests and type casts.
+
+Scala let's you do these using methods defined in class `Any`:
+
+       def isInstanceOf[T]: Boolean  // checks whether this object's type conforms to `T`
+       def asInstanceOf[T]: T        // treats this object as an instance of type `T`
+                                     // throws `ClassCastException` if it isn't.
+
+These correspond to Java's type tests and casts
+
+       Scala                   Java
+
+       x.isInstanceOf[T]       x instanceof T
+       x.asInstanceOf[T]       (T) x
+
+But their use in Scala is discouraged, because there are better alternatives.
+
+Eval with Type Tests and Type Casts
+===================================
+
+Here's a formulation of the `eval` method using type tests and casts:
+
+        def eval(e: Expr): Int =
+          if e.isInstanceOf[Number] then
+            e.asInstanceOf[Number].numValue
+          else if e.isInstanceOf[Sum] then
+            eval(e.asInstanceOf[Sum].leftOp)
+            + eval(e.asInstanceOf[Sum].rightOp)
+          else throw new Error("Unknown expression " + e)
+
+Assessment of this solution:
+->
+\begin{tabular}{lp{10cm}}
+ + & no need for classification methods, access methods only for classes where the value is defined.
+\\
+ -- & low-level and potentially unsafe.
+\end{tabular}
+
+Solution 1: Object-Oriented Decomposition
+=========================================
+
+For example, suppose that all you want to do is _evaluate_ expressions.
+
+You could then define:
+
+      trait Expr
+        def eval: Int
+
+      class Number(n: Int) extends Expr
+        def eval: Int = n
+
+      class Sum(e1: Expr, e2: Expr) extends Expr
+        def eval: Int = e1.eval + e2.eval
+
+But what happens if you'd like to display expressions now?
+
+You have to define new methods in all the subclasses.
+
+Limitations of OO Decomposition
+===============================
+
+And what if you want to simplify the expressions, say using the rule:
+
+      a * b + a * c   ->   a * (b + c)
+
+\red{Problem}: This is a non-local simplification. It cannot be encapsulated in the
+method of a single object.
+
+You are back to square one; you need test and access methods for all the different
+subclasses.
+
diff --git a/lectures/progfun1-4-5.md b/lectures/progfun1-4-5.md
new file mode 100644
index 0000000000000000000000000000000000000000..b0d8930fb1259b15f17f69b08224d47de2929e2c
--- /dev/null
+++ b/lectures/progfun1-4-5.md
@@ -0,0 +1,151 @@
+% Higher-Order Functions
+%
+%
+Higher-Order Functions
+======================
+
+Functional languages treat functions as _first-class values_.
+
+This means that, like any other value, a function
+can be passed as a parameter and returned as a result.
+
+This provides a flexible way to compose programs.
+
+Functions that take other functions as parameters or that return functions
+as results are called _higher order functions_.
+
+Example:
+========
+
+Take the sum of the integers between `a` and `b`:
+
+      def sumInts(a: Int, b: Int): Int =
+        if a > b then 0 else a + sumInts(a + 1, b)
+
+Take the sum of the cubes of all the integers between `a`
+and `b` :
+
+      def cube(x: Int): Int = x * x * x
+
+      def sumCubes(a: Int, b: Int): Int =
+        if a > b then 0 else cube(a) + sumCubes(a + 1, b)
+
+Example (ctd)
+=============
+
+Take the sum of the factorials of all the integers between `a`
+and `b` :
+
+      def sumFactorials(a: Int, b: Int): Int =
+        if a > b then 0 else fact(a) + sumFactorials(a + 1, b)
+
+
+These are special cases of
+$$
+   \sum^b_{n=a} f(n)
+$$
+for different values of $f$.
+
+Can we factor out the common pattern?
+
+Summing with Higher-Order Functions
+===================================
+
+Let's define:
+
+      def sum(f: Int => Int, a: Int, b: Int): Int =
+        if a > b then 0
+        else f(a) + sum(f, a + 1, b)
+
+We can then write:
+
+      def sumInts(a: Int, b: Int)       = sum(id, a, b)
+      def sumCubes(a: Int, b: Int)      = sum(cube, a, b)
+      def sumFactorials(a: Int, b: Int) = sum(fact, a, b)
+
+where
+
+      def id(x: Int): Int   = x
+      def cube(x: Int): Int = x * x * x
+      def fact(x: Int): Int = if x == 0 then 1 else x * fact(x - 1)
+
+Function Types
+==============
+
+The type `A => B` is the type of a \red{function} that
+takes an argument of type `A` and returns a result of
+type `B`.
+
+So, `Int => Int` is the type of functions that map integers to integers.
+
+Anonymous Functions
+===================
+
+Passing functions as parameters leads to the creation of many small functions.
+
+ - Sometimes it is tedious to have to define (and name) these functions using `def`.
+
+Compare to strings: We do not need to define a string using `def`. Instead of
+
+       def str = "abc"; println(str)
+
+We can directly write
+
+       println("abc")
+
+because strings exist as _literals_. Analogously we would like function literals, which let us write a function without giving it a name.
+
+These are called _anonymous functions_.
+
+Anonymous Function Syntax
+=========================
+
+\example: A function that raises its argument to a cube:
+
+      (x: Int) => x * x * x
+
+Here, `(x: Int)` is the \red{parameter} of the function, and
+`x * x * x` is it's \red{body}.
+
+- The type of the parameter can be omitted if it can be inferred by the
+compiler from the context.
+
+If there are several parameters, they are separated by commas:
+
+      (x: Int, y: Int) => x + y
+
+Anonymous Functions are Syntactic Sugar
+=======================================
+
+An anonymous function $(\btt x_1: T_1, ..., x_n: T_n) \Rightarrow E$
+can always be expressed using `def` as follows:
+
+$$
+\btt { def\ f(x_1: T_1, ..., x_n: T_n) = E ; f }
+$$
+
+where $\btt f$ is an arbitrary, fresh name (that's not yet used in the program).
+
+ - One can therefore say that anonymous functions are _syntactic sugar_.
+
+Summation with Anonymous Functions
+==================================
+
+Using anonymous functions, we can write sums in a shorter way:
+
+      def sumInts(a: Int, b: Int)  = sum(x => x, a, b)
+      def sumCubes(a: Int, b: Int) = sum(x => x * x * x, a, b)
+
+Exercise
+========
+
+\quiz
+
+The `sum` function uses linear recursion. Write a tail-recursive version by replacing the `???`s.
+
+      def sum(f: Int => Int, a: Int, b: Int): Int =
+        def loop(a: Int, acc: Int): Int =
+          if ??? then ???
+          else loop(???, ???)
+        loop(???, ???)
+
diff --git a/lectures/progfun1-4-6-pattern-matching.md b/lectures/progfun1-4-6-pattern-matching.md
new file mode 100644
index 0000000000000000000000000000000000000000..791dc7335f7a645316aa4fb29b455f2885c0bcf4
--- /dev/null
+++ b/lectures/progfun1-4-6-pattern-matching.md
@@ -0,0 +1,211 @@
+% Pattern Matching
+%
+%
+Reminder: Decomposition
+=======================
+
+The task we are trying to solve is find a general and convenient way to access
+objects in a extensible class hierarchy.
+
+\vspace{3cm}
+
+
+
+\red{Attempts seen previously}:
+
+- _Classification and access methods_: quadratic explosion
+
+- _Type tests and casts_: unsafe, low-level
+
+- _Object-oriented decomposition_: does not always work, need to touch all classes to add a new method.
+
+
+Solution 2: Functional Decomposition with Pattern Matching
+==========================================================
+
+Observation: the sole purpose of test and accessor functions is to
+\red{reverse} the construction process:
+
+ - Which subclass was used?
+ - What were the arguments of the constructor?
+
+This situation is so common that many functional languages, Scala included, automate it.
+
+Case Classes
+============
+
+A _case class_ definition is similar to a normal class definition, except
+that it is preceded by the modifier `case`. For example:
+
+      trait Expr
+      case class Number(n: Int) extends Expr
+      case class Sum(e1: Expr, e2: Expr) extends Expr
+
+Like before, this defines a trait `Expr`, and two
+concrete subclasses `Number` and `Sum`.
+
+Case Classes (2)
+================
+
+It also implicitly defines companion objects with `apply` methods.
+
+      object Number
+        def apply(n: Int) = new Number(n)
+
+      object Sum
+        def apply(e1: Expr, e2: Expr) = new Sum(e1, e2)
+
+so you can write `Number(1)` instead of `new Number(1)`.
+
+However, these classes are now empty. So how can we access the members?
+
+Pattern Matching
+================
+
+_Pattern matching_ is a generalization of `switch` from
+C/Java to class hierarchies.
+
+It's expressed in Scala using the keyword `match`.
+
+\example
+
+      def eval(e: Expr): Int = e match
+        case Number(n) => n
+        case Sum(e1, e2) => eval(e1) + eval(e2)
+
+Match Syntax
+============
+
+Rules:
+
+ - `match` is followed by a sequence of \red{cases}, `pat => expr`.
+ - Each case associates an \red{expression} `expr` with a \red{pattern} `pat`.
+ - A `MatchError` exception is thrown if no pattern matches the
+value of the selector.
+
+Forms of Patterns
+=================
+
+Patterns are constructed from:
+
+ -  _constructors_, e.g. `Number`, `Sum`,
+ -  _variables_, e.g. `n`, `e1`, `e2`,
+ -  _wildcard patterns_ `_`,
+ -  _constants_, e.g. `1`, `true`.
+
+Variables always begin with a lowercase letter.
+
+The same variable name can only appear once in a pattern. So,
+`Sum(x, x)` is not a legal pattern.
+
+Names of constants begin with a capital letter,
+with the exception of the reserved words `null`, `true`,
+`false`.
+
+Evaluating Match Expressions
+============================
+
+An expression of the form
+$$\btt
+e\ match\ \{\ case\ p_1 => e_1\ ...\ case\ p_n => e_n\ \}
+$$
+matches the value of the selector $\btt e$ with the patterns
+$\btt p_1, ..., p_n$ in the order in which they are written.
+
+The whole match expression is rewritten to the right-hand side of the first
+case where the pattern matches the selector $e$.
+
+References to pattern variables are replaced by the corresponding
+parts in the selector.
+
+What Do Patterns Match?
+=======================
+
+ -  A constructor pattern $\btt C(p_1 , ..., p_n)$ matches
+  all the values of type $\btt C$ (or a subtype) that have been
+  constructed with arguments matching the patterns $\btt p_1, ..., p_n$.
+ -  A variable pattern $\btt x$ matches any value, and
+  \red{binds} the name of the variable to this value.
+ -  A constant pattern $\btt c$ matches values that are equal to
+  $\btt c$ (in the sense of `==`)
+
+
+Example
+=======
+\example
+
+
+          eval(Sum(Number(1), Number(2)))
+
+$\rightarrow$
+
+          Sum(Number(1), Number(2)) match
+            case Number(n) => n
+            case Sum(e1, e2) => eval(e1) + eval(e2)
+
+$\rightarrow$
+
+          eval(Number(1)) + eval(Number(2))
+
+Example (2)
+===========
+
+$\rightarrow$
+
+          Number(1) match
+            case Number(n) => n
+            case Sum(e1, e2) => eval(e1) + eval(e2)
+          + eval(Number(2))
+
+$\rightarrow$
+
+          1 + eval(Number(2))
+
+$\rightarrow\dhd$
+
+          3
+
+Pattern Matching and Methods
+============================
+
+Of course, it's also possible to define the evaluation function as a
+method of the base trait.
+
+\example
+
+      trait Expr
+        def eval: Int = this match
+          case Number(n) => n
+          case Sum(e1, e2) => e1.eval + e2.eval
+
+Exercise
+========
+
+Write a function `show` that uses pattern
+matching to return the representation of a given
+expressions as a string.
+
+      def show(e: Expr): String = ???
+
+Exercise (Optional, Harder)
+===========================
+
+Add case classes `Var` for variables `x` and
+`Prod` for products `x * y` as discussed previously.
+
+Change your `show` function so that it also deals with products.
+
+Pay attention you get operator precedence right but to use as few
+parentheses as possible.
+
+\example
+
+      Sum(Prod(2, Var("x")), Var("y"))
+
+should print as "`2 * x + y`". But
+
+      Prod(Sum(2, Var("x")), Var("y"))
+
+should print as "`(2 + x) * y`".
+
+
diff --git a/lectures/progfun1-4-6.md b/lectures/progfun1-4-6.md
new file mode 100644
index 0000000000000000000000000000000000000000..62b771dc1def0a48aef306f6e0f069b9d721fd75
--- /dev/null
+++ b/lectures/progfun1-4-6.md
@@ -0,0 +1,164 @@
+% Currying
+%
+%
+Motivation
+==========
+
+Look again at the summation functions:
+
+      def sumInts(a: Int, b: Int)       = sum(x => x, a, b)
+      def sumCubes(a: Int, b: Int)      = sum(x => x * x * x, a, b)
+      def sumFactorials(a: Int, b: Int) = sum(fact, a, b)
+
+\question
+
+Note that `a` and `b` get passed unchanged from `sumInts` and `sumCubes` into `sum`.
+
+Can we be even shorter by getting rid of these parameters?
+
+
+
+Functions Returning Functions
+=============================
+
+Let's rewrite `sum` as follows.
+
+      def sum(f: Int => Int): (Int, Int) => Int =
+        def sumF(a: Int, b: Int): Int =
+          if a > b then 0
+          else f(a) + sumF(a + 1, b)
+        sumF
+
+`sum` is now a function that returns another function.
+
+The returned function `sumF` applies the given function parameter `f` and sums the results.
+
+
+Stepwise Applications
+=====================
+
+We can then define:
+
+      def sumInts       = sum(x => x)
+      def sumCubes      = sum(x => x * x * x)
+      def sumFactorials = sum(fact)
+
+These functions can in turn be applied like any other function:
+
+      sumCubes(1, 10) + sumFactorials(10, 20)
+
+Consecutive Stepwise Applications
+=================================
+
+In the previous example, can we avoid the `sumInts`, `sumCubes`, ... middlemen?
+
+Of course:
+
+      sum (cube) (1, 10)
+->
+- `sum(cube)` applies `sum` to `cube` and returns
+the _sum of cubes_ function.
+
+- `sum(cube)` is therefore equivalent to `sumCubes`.
+
+- This function is next applied to the arguments `(1, 10)`.
+->
+Generally, function application associates to the left:
+
+       sum(cube)(1, 10)   ==   (sum (cube)) (1, 10)
+
+Multiple Parameter Lists
+========================
+
+The definition of functions that return functions is so useful in
+functional programming that there is a special syntax for it in Scala.
+
+For example, the following definition of `sum` is equivalent to
+the one with the nested `sumF` function, but shorter:
+
+      def sum(f: Int => Int)(a: Int, b: Int): Int =
+        if a > b then 0 else f(a) + sum(f)(a + 1, b)
+
+Expansion of Multiple Parameter Lists
+=====================================
+
+In general, a definition of a function with multiple parameter lists
+
+$$\btt
+      def\ f (args_1) ... (args_n) = E
+$$
+
+where $\btt n > 1$, is equivalent to
+$$\btt
+      def\ f (args_1) ... (args_{n-1}) = \{ def\ g (args_n) = E ; g \}
+$$
+
+where `g` is a fresh identifier.
+Or for short:
+
+$$\btt
+      def\ f (args_1) ... (args_{n-1}) = ( args_n \Rightarrow E )
+$$
+
+Expansion of Multiple Parameter Lists (2)
+=========================================
+
+By repeating the process $n$ times
+$$\btt
+      def\ f (args_1) ... (args_{n-1}) (args_n) = E
+$$
+is shown to be equivalent to
+$$\btt
+      def\ f = (args_1 \Rightarrow ( args_2 \Rightarrow ... ( args_n \Rightarrow E ) ... ))
+$$
+
+This style of definition and function application is called _currying_,
+named for its instigator, Haskell Brooks Curry (1900-1982), a twentieth century logician.
+
+In fact, the idea goes back even further to Schönfinkel and Frege, but the term ``currying" has stuck.
+
+More Function Types
+===================
+
+Question: Given,
+
+      def sum(f: Int => Int)(a: Int, b: Int): Int = ...
+
+What is the type of `sum` ?
+
+More Function Types
+===================
+
+Question: Given,
+
+      def sum(f: Int => Int)(a: Int, b: Int): Int = ...
+
+What is the type of `sum` ?
+
+\boldred{Answer:}
+
+      (Int => Int) => (Int, Int) => Int
+
+Note that functional types associate to the right. That is to say that
+
+        Int => Int => Int
+
+is equivalent to
+
+        Int => (Int => Int)
+
+Exercise
+========
+
+\quiz
+
+ 1. Write a `product` function that calculates the product of
+    the values of a function for the points on a given interval.
+
+ 2. Write `factorial` in terms of `product`.
+
+ 3. Can you write a more general function, which generalizes
+    both `sum` and `product`?
+
+
+
diff --git a/lectures/progfun1-4-7-syntax.md b/lectures/progfun1-4-7-syntax.md
new file mode 100644
index 0000000000000000000000000000000000000000..ad1e9c7acef61e240830e779bc4c00198ef3da2f
--- /dev/null
+++ b/lectures/progfun1-4-7-syntax.md
@@ -0,0 +1,93 @@
+% Scala Syntax Summary
+%
+%
+
+Language Elements Seen So Far:
+==============================
+
+We have seen language elements to express types, expressions
+and definitions.
+
+Below, we give their context-free syntax in Extended Backus-Naur form (EBNF),
+where
+
+$\gap$   `|` denotes an alternative, \par
+
+$\gap$   `[...]` an option (0 or 1), \par
+
+$\gap$   `{...}` a repetition (0 or more).
+
+Types
+=====
+
+\begin{lstlisting}
+Type         = SimpleType | FunctionType
+FunctionType = SimpleType `=>' Type
+             | `(' [Types] `)' `=>' Type
+SimpleType   = Ident
+Types        = Type {`,' Type}
+\end{lstlisting}
+
+A \red{type} can be:
+
+ - A \red{numeric type}: `Int`, `Double` (and `Byte`, `Short`, `Char`, `Long`, `Float`),
+ - The `Boolean` type with the values `true` and `false`,
+ - The `String` type,
+ - A \red{function type}, like `Int => Int`, `(Int, Int) => Int`.
+
+Later we will see more forms of types.
+
+Expressions
+===========
+
+\begin{lstlisting}
+Expr         = InfixExpr | FunctionExpr
+             | if `(' Expr `)' Expr else Expr
+InfixExpr    = PrefixExpr | InfixExpr Operator InfixExpr
+Operator     = ident
+PrefixExpr   = [`+' | `-' | `!' | `~' ] SimpleExpr
+SimpleExpr   = ident | literal | SimpleExpr `.' ident
+             | Block
+FunctionExpr = Bindings `=>` Expr
+Bindings     = ident [`:' SimpleType]
+             | `(' [Binding {`,' Binding}] `)'
+Binding      = ident [`:' Type]
+Block        = `{' {Def `;'} Expr `}'
+\end{lstlisting}
+
+Expressions (2)
+===============
+
+An \red{expression} can be:
+
+- An \red{identifier} such as `x`, `isGoodEnough`,
+- A \red{literal}, like `0`, `1.0`, `"abc"`,
+- A \red{function application}, like `sqrt(x)`,
+- An \red{operator application}, like `-x`, `y + x`,
+- A \red{selection}, like `math.abs`,
+- A \red{conditional expression}, like `if x < 0 then -x else x`,
+- A \red{block}, like `{ val x = math.abs(y) ; x * 2 }`
+- An \red{anonymous function}, like `x => x + 1`.
+
+Definitions
+===========
+
+\begin{lstlisting}
+Def          = FunDef  |  ValDef
+FunDef       = def ident {`(' [Parameters] `)'}
+               [`:' Type] `=' Expr
+ValDef       = val ident [`:' Type] `=' Expr
+Parameter    = ident `:' [ `=>' ] Type
+Parameters   = Parameter {`,' Parameter}
+\end{lstlisting}
+
+A \red{definition} can be:
+
+- A \red{function definition}, like `def square(x: Int) = x * x`
+- A \red{value definition}, like `val y = square(2)`
+
+A \red{parameter} can be:
+
+- A \red{call-by-value parameter}, like `(x: Int)`,
+- A \red{call-by-name parameter}, like `(y: => Double)`.
+
diff --git a/lectures/progfun1-4-7.md b/lectures/progfun1-4-7.md
new file mode 100644
index 0000000000000000000000000000000000000000..0c191802cbb2ba2c9a46449085cf4d4988673d8e
--- /dev/null
+++ b/lectures/progfun1-4-7.md
@@ -0,0 +1,152 @@
+% Example: Finding Fixed Points
+%
+%
+Finding a fixed point of a function
+===================================
+
+A number `x` is called a \red{fixed point} of a function `f` if
+
+      f(x) = x
+
+For some functions `f` we can locate the fixed points by
+starting with an initial estimate and then by applying `f` in a repetitive way.
+
+      x, f(x), f(f(x)), f(f(f(x))), ...
+
+until the value does not vary anymore (or the change is sufficiently
+small).
+
+Programmatic Solution
+=====================
+
+This leads to the following function for finding a fixed point:
+
+      val tolerance = 0.0001
+      def isCloseEnough(x: Double, y: Double) =
+        abs((x - y) / x) / x < tolerance
+      def fixedPoint(f: Double => Double)(firstGuess: Double) =
+        def iterate(guess: Double): Double =
+          val next = f(guess)
+          if isCloseEnough(guess, next) then next
+          else iterate(next)
+        iterate(firstGuess)
+
+Return to Square Roots
+======================
+
+Here is a _specification_ of the `sqrt` function:
+
+$\gap$ `sqrt(x)` $=$ the number `y` such that `y * y = x`.
+
+Or, by dividing both sides of the equation with `y`:
+
+$\gap$ `sqrt(x)` $=$ the number `y` such that  `y = x / y`.
+
+Consequently, `sqrt(x)` is a fixed point of the function `(y => x / y)`.
+
+First Attempt
+=============
+
+This suggests to calculate `sqrt(x)` by iteration towards a fixed point:
+
+      def sqrt(x: Double) =
+        fixedPoint(y => x / y)(1.0)
+
+Unfortunately, this does not converge.
+
+Let's add a `println` instruction to the function `fixedPoint` so
+we can follow the current value of `guess`:
+
+First Attempt (2)
+=================
+
+      def fixedPoint(f: Double => Double)(firstGuess: Double) =
+
+        def iterate(guess: Double): Double =
+          val next = f(guess)
+          println(next)
+          if isCloseEnough(guess, next) then next
+          else iterate(next)
+
+        iterate(firstGuess)
+
+`sqrt(2)` then produces:
+
+        2.0
+        1.0
+        2.0
+        1.0
+        ...
+
+Average Damping
+===============
+
+One way to control such oscillations is to prevent the estimation from
+varying too much.  This is done by _averaging_ successive values of the
+original sequence:
+
+       def sqrt(x: Double) = fixedPoint(y => (y + x / y) / 2)(1.0)
+
+This produces
+
+      1.5
+      1.4166666666666665
+      1.4142156862745097
+      1.4142135623746899
+      1.4142135623746899
+
+In fact, if we expand the fixed point function `fixedPoint` we find a similar
+square root function to what we developed last week.
+
+Functions as Return Values
+==========================
+
+The previous examples have shown that the expressive power of a
+language is greatly increased if we can pass function arguments.
+
+The following example shows that functions that return functions
+can also be very useful.
+
+Consider again iteration towards a fixed point.
+
+We begin by observing that $\sqrt x$ is a fixed point of the function
+`y => x / y`.
+
+Then, the iteration converges by averaging successive values.
+
+This technique of _stabilizing by averaging_ is general enough to
+merit being abstracted into its own function.
+
+      def averageDamp(f: Double => Double)(x: Double) = (x + f(x)) / 2
+
+Exercise:
+=========
+
+Write a square root function using
+`fixedPoint` and `averageDamp`.
+
+\quiz
+
+Final Formulation of Square Root
+================================
+
+      def sqrt(x: Double) = fixedPoint(averageDamp(y => x/y))(1.0)
+
+This expresses the elements of the algorithm as clearly as possible.
+
+Summary
+=======
+
+We saw last week that the functions are essential abstractions because
+they allow us to introduce general methods to perform computations as
+explicit and named elements in our programming language.
+
+This week, we've seen that these abstractions can be combined with
+higher-order functions to create new abstractions.
+
+As a programmer, one must look for opportunities to abstract and
+reuse.
+
+The highest level of abstraction is not always the best, but it
+is important to know the techniques of abstraction, so as to
+use them when appropriate.
diff --git a/lectures/progfun1-5-0.md b/lectures/progfun1-5-0.md
new file mode 100644
index 0000000000000000000000000000000000000000..52ef6328539db2e6a9f2ebe8ab541d5056c7e928
--- /dev/null
+++ b/lectures/progfun1-5-0.md
@@ -0,0 +1,192 @@
+% Lists
+%
+%
+Lists
+=====
+
+The list is a fundamental data structure in functional programming.
+
+A list having $\btt x_1, ..., x_n$
+as elements is written `List(`$\btt x_1, ..., x_n$`)`
+
+\example
+
+      val fruit  = List("apples", "oranges", "pears")
+      val nums   = List(1, 2, 3, 4)
+      val diag3  = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1))
+      val empty  = List()
+
+There are two important differences between lists and arrays.
+
+ - Lists are immutable --- the elements of a list cannot be changed.
+ - Lists are recursive, while arrays are flat.
+
+Lists
+=====
+
+      val fruit  = List("apples", "oranges", "pears")
+      val diag3  = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1))
+
+The List Type
+=============
+
+Like arrays, lists are \red{\em homogeneous}: the elements of a
+list must all have the same type.
+
+The type of a list with elements of type `T` is written `scala.List[T]` or shorter just `List[T]`
+
+\example
+
+      val fruit: List[String]    = List("apples", "oranges", "pears")
+      val nums : List[Int]       = List(1, 2, 3, 4)
+      val diag3: List[List[Int]] = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1))
+      val empty: List[Nothing]   = List()
+
+Constructors of Lists
+=====================
+
+All lists are constructed from:
+
+ - the empty list `Nil`, and
+ - the construction operation `::` (pronounced \emph{cons}): \newline
+`x :: xs` gives a new list with the first element `x`, followed by the elements of `xs`.
+
+For example:
+
+      fruit = "apples" :: ("oranges" :: ("pears" :: Nil))
+      nums  = 1 :: (2 :: (3 :: (4 :: Nil)))
+      empty = Nil
+
+
+Right Associativity
+===================
+
+Convention: Operators ending in "`:`" associate to the right.
+
+\gap `A :: B :: C` is interpreted as `A :: (B :: C)`.
+
+We can thus omit the parentheses in the definition above.
+
+\example
+
+      val nums = 1 :: 2 :: 3 :: 4 :: Nil
+
+Operators ending in "`:`" are also different in the they are seen as method calls of the _right-hand_ operand.
+
+So the expression above is equivalent to
+
+      Nil.::(4).::(3).::(2).::(1)
+
+Operations on Lists
+===================
+
+All operations on lists can be expressed in terms of the following
+three operations:
+
+\begin{tabular}{ll}
+\verb`head`  &  the first element of the list \\
+\verb`tail`  &  the list composed of all the elements except the first. \\
+\verb`isEmpty` & `true` if the list is empty, `false` otherwise.
+\end{tabular}
+
+These operations are defined as methods of objects of type list.
+For example:
+
+      fruit.head      == "apples"
+      fruit.tail.head == "oranges"
+      diag3.head      == List(1, 0, 0)
+      empty.head      == throw new NoSuchElementException("head of empty list")
+
+List Patterns
+=============
+
+It is also possible to decompose lists with pattern matching.
+
+\begin{tabular}{lp{9cm}}
+      \verb`Nil`     &  The \verb`Nil` constant \\
+      \verb`p :: ps` &  A pattern that matches a list with a \verb`head` matching \verb`p` and a
+                        \verb`tail` matching \verb`ps`. \\
+      \verb`List(p1, ..., pn)` & same as \verb`p1 :: ... :: pn :: Nil`
+\end{tabular}
+
+\example
+
+\begin{tabular}{lp{9cm}}
+      \verb`1 :: 2 :: xs`       & Lists of that start with \verb`1` and then \verb`2`
+\\    \verb`x :: Nil`           & Lists of length 1
+\\    \verb`List(x)`            & Same as \verb`x :: Nil`
+\\    \verb`List()`             & The empty list, same as \verb`Nil`
+\\    \verb`List(2 :: xs)`      & A list that contains as only element another list
+                                  that starts with \verb`2`.
+\end{tabular}
+
+Exercise
+========
+
+Consider the pattern `x :: y :: List(xs, ys) :: zs`.
+
+What is the condition that describes most accurately the length `L`
+of the lists it matches?
+
+      O         L == 3
+      O         L == 4
+      O         L == 5
+      O         L >= 3
+      O         L >= 4
+      O         L >= 5
+->
+\quiz
+
+Sorting Lists
+=============
+
+Suppose we want to sort a list of numbers in ascending order:
+
+ -  One way to sort the list `List(7, 3, 9, 2)` is to sort the
+  tail `List(3, 9, 2)` to obtain `List(2, 3, 9)`.
+ -  The next step is to insert the head `7` in the right place
+  to obtain the result `List(2, 3, 7, 9)`.
+
+This idea describes \red{Insertion Sort} :
+
+      def isort(xs: List[Int]): List[Int] = xs match {
+        case List() => List()
+        case y :: ys => insert(y, isort(ys))
+      }
+
+Exercise
+========
+
+Complete the definition insertion sort by filling in the `???`s in the definition below:
+
+      def insert(x: Int, xs: List[Int]): List[Int] = xs match {
+        case List() => ???
+        case y :: ys => ???
+      }
+
+What is the worst-case complexity of insertion sort relative to the length of the input list `N`?
+
+      O      the sort takes constant time
+      O      proportional to N
+      O      proportional to N log(N)
+      O      proportional to N * N
+
+\quiz
+
+Exercise
+========
+
+Complete the definition insertion sort by filling in the `???`s in the definition below:
+
+      def insert(x: Int, xs: List[Int]): List[Int] = xs match {
+        case List() =>
+        case y :: ys =>
+      }
+
+What is the worst-case complexity of insertion sort relative to the length of the input list `N`?
+
+      O      the sort takes constant time
+      O      proportional to N
+      O      proportional to N * log(N)
+      O      proportional to N * N
+
diff --git a/lectures/progfun1-5-1.md b/lectures/progfun1-5-1.md
new file mode 100644
index 0000000000000000000000000000000000000000..d62ba5a934313f91fa8c1e1c7a0a8d082d152199
--- /dev/null
+++ b/lectures/progfun1-5-1.md
@@ -0,0 +1,231 @@
+% More Functions on Lists
+%
+%
+
+List Methods (1)
+================
+
+\red{Sublists and element access:}
+
+\begin{tabular}{lp{8cm}}
+   \verb`  xs.length     `  & The number of elements of \verb`xs`.
+\\ \verb`  xs.last`    & The list's last element, exception if \verb`xs` is empty.
+\\ \verb`  xs.init`    & A list consisting of all elements of \verb`xs` except the last one,
+                         exception if \verb`xs` is empty.
+\\ \verb`  xs take n` & A list consisting of the first \verb`n` elements of \verb`xs`, or \verb`xs` itself
+                         if it is shorter than \verb`n`.
+\\ \verb`  xs drop n` & The rest of the collection after taking \verb`n` elements.
+\\ \verb`  xs(n)`     & (or, written out, \verb`xs apply n`). The element of \verb`xs` at index \verb`n`.
+
+\end{tabular}
+
+List Methods (2)
+================
+
+\red{Creating new lists:}
+
+\begin{tabular}{lp{8cm}}
+   \verb`  xs ++ ys`        & The list consisting of all elements of \verb`xs`
+                              followed by all elements of \verb`ys`.
+\\ \verb`  xs.reverse`      & The list containing the elements of \verb`xs` in reversed order.
+\\ \verb`  xs updated (n, x)` & The list containing the same elements as \verb`xs`, except at index
+                            \verb`n` where it contains \verb`x`.
+\end{tabular}
+
+\red{Finding elements:}
+
+\begin{tabular}{lp{8cm}}
+   \verb`  xs indexOf x `& The index of the first element in \verb`xs` equal to \verb`x`, or \verb`-1` if \verb`x`
+                          does not appear in \verb`xs`.
+\\ \verb`  xs contains x    ` & same as \verb`xs indexOf x >= 0`
+\end{tabular}
+
+Implementation of `last`
+========================
+
+The complexity of `head` is (small) constant time.
+
+What is the complexity of `last`?
+
+To find out, let's write a possible implementation of `last` as a stand-alone function.
+
+      def last[T](xs: List[T]): T = xs match {
+        case List() => throw new Error("last of empty list")
+        case List(x) =>
+        case y :: ys =>
+      }
+
+Implementation of `last`
+========================
+
+The complexity of `head` is (small) constant time.
+
+What is the complexity of `last`?
+
+To find out, let's write a possible implementation of `last` as a stand-alone function.
+
+      def last[T](xs: List[T]): T = xs match {
+        case List() => throw new Error("last of empty list")
+        case List(x) => x
+        case y :: ys =>
+      }
+
+Implementation of `last`
+========================
+
+The complexity of `head` is (small) constant time.
+
+What is the complexity of `last`?
+
+To find out, let's write a possible implementation of `last` as a stand-alone function.
+
+      def last[T](xs: List[T]): T = xs match {
+        case List() => throw new Error("last of empty list")
+        case List(x) => x
+        case y :: ys => last(ys)
+      }
+->
+So, `last` takes steps proportional to the length of the list `xs`.
+
+Exercise
+========
+
+Implement `init` as an external function, analogous to `last`.
+
+      def init[T](xs: List[T]): List[T] = xs match {
+        case List() => throw new Error("init of empty list")
+        case List(x) => ???
+        case y :: ys => ???
+      }
+
+\quiz
+
+Exercise
+========
+
+Implement `init` as an external function, analogous to `last`.
+
+      def init[T](xs: List[T]): List[T] = xs match {
+        case List() => throw new Error("init of empty list")
+        case List(x) =>
+        case y :: ys =>
+      }
+
+\quiz
+
+Implementation of Concatenation
+===============================
+
+How can concatenation be implemented?
+
+Let's try by writing a stand-alone function:
+
+      def concat[T](xs: List[T], ys: List[T]) =
+
+Implementation of Concatenation
+===============================
+
+How can concatenation be implemented?
+
+Let's try by writing a stand-alone function:
+
+      def concat[T](xs: List[T], ys: List[T]) = xs match {
+        case List() =>
+        case z :: zs =>
+      }
+
+Implementation of Concatenation
+===============================
+
+How can concatenation be implemented?
+
+Let's try by writing a stand-alone function:
+
+      def concat[T](xs: List[T], ys: List[T]) = xs match {
+        case List() => ys
+        case z :: zs =>
+      }
+
+Implementation of Concatenation
+===============================
+
+How can concatenation be implemented?
+
+Let's try by writing a stand-alone function:
+
+      def concat[T](xs: List[T], ys: List[T]) = xs match {
+        case List() => ys
+        case z :: zs => z :: concat(zs, ys)
+      }
+
+->
+What is the complexity of `concat`?
+
+
+Implementation of `reverse`
+===========================
+
+How can reverse be implemented?
+
+Let's try by writing a stand-alone function:
+
+     def reverse[T](xs: List[T]): List[T] = xs match {
+       case List() =>
+       case y :: ys =>
+     }
+
+Implementation of `reverse`
+===========================
+
+How can reverse be implemented?
+
+Let's try by writing a stand-alone function:
+
+     def reverse[T](xs: List[T]): List[T] = xs match {
+       case List() => List()
+       case y :: ys =>
+     }
+
+
+
+Implementation of `reverse`
+===========================
+
+How can reverse be implemented?
+
+Let's try by writing a stand-alone function:
+
+     def reverse[T](xs: List[T]): List[T] = xs match {
+       case List() => List()
+       case y :: ys => reverse(ys) ++ List(y)
+     }
+->
+What is the complexity of `reverse`?
+
+\red{Can we do better?} (to be solved later).
+
+Exercise
+========
+
+Remove the `n`'th element of a list `xs`. If `n` is out of bounds, return `xs` itself.
+
+     def removeAt[T](n: Int, xs: List[T]) = ???
+
+Usage example:
+
+\begin{worksheet}
+ \verb`removeAt(1, List('a', 'b', 'c', 'd'))` \wsf   List(a, c, d)
+\end{worksheet}
+
+Exercise (Harder, Optional)
+===========================
+
+Flatten a list structure:
+
+     def flatten(xs: List[Any]): List[Any] = ???
+
+     flatten(List(List(1, 1), 2, List(3, List(5, 8))))
+                 >   res0: List[Any] = List(1, 1, 2, 3, 5, 8)
+
+
+
diff --git a/lectures/progfun1-5-2.md b/lectures/progfun1-5-2.md
new file mode 100644
index 0000000000000000000000000000000000000000..508f56b31257bbd74f4abe5606b8a5d1aa4b2e35
--- /dev/null
+++ b/lectures/progfun1-5-2.md
@@ -0,0 +1,147 @@
+% Pairs and Tuples
+%
+%
+Sorting Lists Faster
+====================
+
+As a non-trivial example, let's design a function to sort lists
+that is more efficient than insertion sort.
+
+A good algorithm for this is \red{merge sort}. The idea is as
+follows:
+
+If the list consists of zero or one elements, it is already sorted.
+
+Otherwise,
+
+ - Separate the list into two sub-lists, each containing around half of
+the elements of the original list.
+ - Sort the two sub-lists.
+ - Merge the two sorted sub-lists into a single sorted list.
+
+First MergeSort Implementation
+==============================
+
+Here is the implementation of that algorithm in Scala:
+
+      def msort(xs: List[Int]): List[Int] = {
+        val n = xs.length/2
+        if n == 0 then xs
+        else {
+          def merge(xs: List[Int], ys: List[Int]) = ???
+          val (fst, snd) = xs.splitAt(n)
+          merge(msort(fst), msort(snd))
+        }
+      }
+
+Definition of Merge
+===================
+
+Here is a definition of the `merge` function:
+
+      def merge(xs: List[Int], ys: List[Int]) =
+        xs match {
+          case Nil =>
+            ys
+          case x :: xs1 =>
+            ys match {
+              case Nil =>
+                xs
+              case y :: ys1 =>
+                if x < y then x :: merge(xs1, ys)
+                else y :: merge(xs, ys1)
+            }
+          }
+
+The SplitAt Function
+====================
+
+The `splitAt` function on lists returns two sublists
+
+ - the elements up the the given index
+ - the elements from that index
+
+The lists are returned in a \red{pair}.
+
+
+Detour: Pair and Tuples
+=======================
+
+The pair consisting of `x` and `y` is written `(x, y)` in Scala.
+
+\example
+
+\begin{worksheet}
+\verb`  val pair = ("answer", 42)` \wsf pair  : (String, Int) = (answer,42)
+\end{worksheet}
+
+The type of `pair` above is `(String, Int)`.
+
+Pairs can also be used as patterns:
+
+\begin{worksheet}
+\verb`  val (label, value) = pair` \wsf  label  : String = answer \\
+                                 \wsn  value  : Int = 42
+\end{worksheet}
+
+This works analogously for tuples with more than two elements.
+
+Translation of Tuples
+=====================
+
+A tuple type $\btt (T_1, ..., T_n)$ is an abbreviation of the
+parameterized type
+
+$$\btt
+  scala.Tuple{\it n}[T_1, ..., T_n]
+$$
+
+A tuple expression $\btt (e_1, ..., e_n)$
+is equivalent to the function application
+
+$$\btt
+   scala.Tuple{\it n}(e_1, ..., e_n)
+$$
+
+A tuple pattern $\btt (p_1, ..., p_n)$
+is equivalent to the constructor pattern
+
+$$\btt
+   scala.Tuple{\it n}(p_1, ..., p_n)
+$$
+
+The Tuple class
+===============
+
+Here, all `Tuple`\mbox{\it n} classes are modeled after the following pattern:
+
+      case class Tuple2[T1, T2](_1: +T1, _2: +T2) {
+        override def toString = "(" + _1 + "," + _2 +")"
+      }
+
+The fields of a tuple can be accessed with names `_1`, `_2`, ...
+
+So instead of the pattern binding
+
+      val (label, value) = pair
+
+one could also have written:
+
+      val label = pair._1
+      val value = pair._2
+
+But the pattern matching form is generally preferred.
+
+Exercise
+========
+
+The `merge` function as given uses a nested pattern match.
+
+This does not reflect the inherent symmetry of the merge algorithm.
+
+Rewrite `merge` using a pattern matching over pairs.
+
+      def merge(xs: List[Int], ys: List[Int]): List[Int] =
+        (xs, ys) match {
+          ???
+        }
diff --git a/lectures/progfun1-5-3.md b/lectures/progfun1-5-3.md
new file mode 100644
index 0000000000000000000000000000000000000000..e0179c514a89b04a4adb6f1bf09a288194b9259e
--- /dev/null
+++ b/lectures/progfun1-5-3.md
@@ -0,0 +1,152 @@
+% Implicit Parameters
+%
+%
+
+Making Sort more General
+========================
+
+Problem: How to parameterize `msort` so that it can also be used for
+lists with elements other than `Int`?
+
+      def msort[T](xs: List[T]): List[T] = ...
+
+does not work, because the comparison `<` in `merge` is not defined
+for arbitrary types `T`.
+
+\red{Idea:} Parameterize `merge` with the necessary comparison function.
+
+Parameterization of Sort
+========================
+
+The most flexible design is to make the function \verb@sort@
+polymorphic and to pass the comparison operation as an additional
+parameter:
+
+      def msort[T](xs: List[T])(lt: (T, T) => Boolean) = {
+        ...
+          merge(msort(fst)(lt), msort(snd)(lt))
+      }
+
+Merge then needs to be adapted as follows:
+
+      def merge(xs: List[T], ys: List[T]) = (xs, ys) match {
+        ...
+        case (x :: xs1, y :: ys1) =>
+          if lt(x, y) then ...
+          else ...
+      }
+
+Calling Parameterized Sort
+==========================
+
+We can now call `msort` as follows:
+
+      val xs = List(-5, 6, 3, 2, 7)
+      val fruits = List("apple", "pear", "orange", "pineapple")
+
+      msort(xs)((x: Int, y: Int) => x < y)
+      msort(fruits)((x: String, y: String) => x.compareTo(y) < 0)
+
+Or, since parameter types can be inferred from the call `msort(xs)`:
+
+      msort(xs)((x, y) => x < y)
+
+
+Parametrization with Ordering
+=============================
+
+There is already a class in the standard library that represents orderings.
+
+      scala.math.Ordering[T]
+
+provides ways to compare elements of type `T`. So instead of
+parameterizing with the `lt` operation directly, we could parameterize
+with `Ordering` instead:
+
+      def msort[T](xs: List[T])(ord: Ordering[T]) =
+
+        def merge(xs: List[T], ys: List[T]) =
+          ... if ord.lt(x, y) then ...
+
+        ... merge(msort(fst)(ord), msort(snd)(ord)) ...
+
+Ordering Instances:
+===================
+
+Calling the new `msort` can be done like this:
+
+      import math.Ordering
+
+      msort(nums)(Ordering.Int)
+      msort(fruits)(Ordering.String)
+
+This makes use of the values `Int` and `String` defined in the
+`scala.math.Ordering` object, which produce the right orderings on
+integers and strings.
+
+Aside: Implicit Parameters
+==========================
+
+\red{Problem:} Passing around `lt` or `ord` values is cumbersome.
+
+We can avoid this by making `ord` an implicit parameter.
+
+      def msort[T](xs: List[T])(implicit ord: Ordering[T]) =
+
+        def merge(xs: List[T], ys: List[T]) =
+          ... if ord.lt(x, y) then ...
+
+        ... merge(msort(fst), msort(snd)) ...
+
+Then calls to `msort` can avoid the ordering parameters:
+
+      msort(nums)
+      msort(fruits)
+
+The compiler will figure out the right implicit to pass based on the
+demanded type.
+
+Rules for Implicit Parameters
+=============================
+
+Say, a function takes an implicit parameter of type `T`.
+
+The compiler will search an implicit definition that
+
+ - is marked `implicit`
+ - has a type compatible with `T`
+ - is visible at the point of the function call, or is defined
+   in a companion object associated with `T`.
+
+If there is a single (most specific) definition, it will be taken as
+actual argument for the implicit parameter.
+
+Otherwise it's an error.
+
+Exercise: Implicit Parameters
+=============================
+
+Consider the following line of the definition of `msort`:
+
+        ... merge(msort(fst), msort(snd)) ...
+
+Which implicit argument is inserted?
+
+      O        Ordering.Int
+      O        Ordering.String
+      O        the "ord" parameter of "msort"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lectures/progfun1-5-4.md b/lectures/progfun1-5-4.md
new file mode 100644
index 0000000000000000000000000000000000000000..6cdb8f9c818e4441f503d609fbe0f329c3627571
--- /dev/null
+++ b/lectures/progfun1-5-4.md
@@ -0,0 +1,162 @@
+% Higher-order List Functions
+%
+%
+
+Recurring Patterns for Computations on Lists
+============================================
+
+
+ The examples have shown that functions on lists often have
+  similar structures.
+
+ We can identify several recurring patterns, like,
+
+ -  transforming each element in a list in a certain way,
+ -  retrieving a list of all elements satisfying a criterion,
+ -  combining the elements of a list using an operator.
+
+Functional languages allow programmers to write generic functions
+that implement patterns such as these using \red{\em higher-order functions}.
+
+Applying a Function to Elements of a List
+=========================================
+
+A common operation is to transform each element of a list and then
+return the list of results.
+
+For example, to multiply each element of a list by the same factor, you could write:
+
+      def scaleList(xs: List[Double], factor: Double): List[Double] = xs match {
+        case Nil     => xs
+        case y :: ys => y * factor :: scaleList(ys, factor)
+      }
+
+Map
+===
+
+
+This scheme can be generalized to the method `map` of the
+`List` class. A simple way to define `map` is as follows:
+
+      abstract class List[T] { ...
+        def map[U](f: T => U): List[U] = this match {
+          case Nil     => this
+          case x :: xs => f(x) :: xs.map(f)
+        }
+
+(in fact, the actual definition of `map` is a bit more complicated,
+because it is tail-recursive, and also because it works for arbitrary
+collections, not just lists).
+
+Using `map`, `scaleList` can be written more concisely.
+
+      def scaleList(xs: List[Double], factor: Double) =
+        xs map (x => x * factor)
+
+Exercise
+========
+
+Consider a function to square each element of a list, and
+return the result. Complete the two following equivalent definitions of
+`squareList`.
+
+
+      def squareList(xs: List[Int]): List[Int] = xs match {
+        case Nil     => ???
+        case y :: ys => ???
+      }
+
+      def squareList(xs: List[Int]): List[Int] =
+        xs map ???
+
+Exercise
+========
+
+Consider a function to square each element of a list, and
+return the result. Complete the two following equivalent definitions of
+`squareList`.
+
+
+      def squareList(xs: List[Int]): List[Int] = xs match {
+        case Nil     =>
+        case y :: ys =>
+      }
+
+      def squareList(xs: List[Int]): List[Int] =
+        xs map
+
+Filtering
+=========
+
+Another common operation on lists is the selection of all elements
+satisfying a given condition. For example:
+
+      def posElems(xs: List[Int]): List[Int] = xs match {
+        case Nil     => xs
+        case y :: ys => if y > 0 then y :: posElems(ys) else posElems(ys)
+      }
+
+Filter
+======
+
+This pattern is generalized by the method `filter` of the `List` class:
+
+      abstract class List[T] {
+        ...
+        def filter(p: T => Boolean): List[T] = this match {
+          case Nil     => this
+          case x :: xs => if p(x) then x :: xs.filter(p) else xs.filter(p)
+        }
+      }
+
+Using `filter`, `posElems` can be written more concisely.
+
+      def posElems(xs: List[Int]): List[Int] =
+        xs filter (x => x > 0)
+
+Variations of Filter
+====================
+
+Besides filter, there are also the following methods that extract
+sublists based on a predicate:
+
+\begin{tabular}{lp{8cm}}
+   \verb`  xs.filterNot(p)`  & Same as \verb`xs filter (x => !p(x))`; The list consisting of those elements of \verb`xs` that do not satisfy the predicate \verb`p`.
+\\ \verb`  xs.partition(p)`  & Same as \verb`(xs filter p, xs filterNot p)`, but computed in a single traversal of the list \verb`xs`.
+\\ \verb`  xs.takeWhile(p)`  & The longest prefix of list \verb`xs` consisting of elements that all satisfy the predicate \verb`p`.
+\\ \verb`  xs.dropWhile(p)`  & The remainder of the list \verb`xs` after any leading elements satisfying \verb`p` have been removed.
+\\ \verb`  xs.span(p)`       & Same as \verb`(xs takeWhile p, xs dropWhile p)` but computed in a single traversal of the list \verb`xs`.
+\end{tabular}
+
+Exercise
+========
+
+Write a function `pack` that packs consecutive duplicates of list elements into sublists. For instance,
+
+      pack(List("a", "a", "a", "b", "c", "c", "a"))
+
+should give
+
+      List(List("a", "a", "a"), List("b"), List("c", "c"), List("a")).
+
+You can use the following template:
+
+      def pack[T](xs: List[T]): List[List[T]] = xs match {
+        case Nil      => Nil
+        case x :: xs1 => ???
+      }
+
+Exercise
+========
+
+Using `pack`, write a function `encode` that produces the run-length
+encoding of a list.
+
+The idea is to encode `n` consecutive duplicates of an element `x` as a pair `(x, n)`. For instance,
+
+      encode(List("a", "a", "a", "b", "c", "c", "a"))
+
+should give
+
+      List(("a", 3), ("b", 1), ("c", 2), ("a", 1)).
+
diff --git a/lectures/progfun1-5-5.md b/lectures/progfun1-5-5.md
new file mode 100644
index 0000000000000000000000000000000000000000..2e1aa8f71eea86a470b1f0b96d36ffaa7668ad1e
--- /dev/null
+++ b/lectures/progfun1-5-5.md
@@ -0,0 +1,203 @@
+% Reduction of Lists
+%
+%
+Reduction of Lists
+==================
+
+Another common operation on lists is to combine the elements of a list using a given operator.
+
+For example:
+
+      sum(List(x1, ..., xn))         =  0 + x1 + ... + xn
+      product(List(x1, ..., xn))     =  1 * x1 * ... * xn
+
+We can implement this with the usual recursive schema:
+
+      def sum(xs: List[Int]): Int = xs match {
+        case Nil     => 0
+        case y :: ys => y + sum(ys)
+      }
+
+ReduceLeft
+==========
+
+This pattern can be abstracted out using the generic method `reduceLeft`:
+
+`reduceLeft` inserts a given binary operator between adjacent elements
+of a list:
+
+      List(x1, ..., xn) reduceLeft op   =  (...(x1 op x2) op ... ) op xn
+
+\vspace{2cm}
+Using `reduceLeft`, we can simplify:
+
+      def sum(xs: List[Int])     = (0 :: xs) reduceLeft ((x, y) => x + y)
+      def product(xs: List[Int]) = (1 :: xs) reduceLeft ((x, y) => x * y)
+
+A Shorter Way to Write Functions
+================================
+
+Instead of `((x, y) => x * y))`, one can also write shorter:
+
+      (_ * _)
+
+Every `_` represents a new parameter, going from left to right.
+
+The parameters are defined at the next outer pair of parentheses (or the whole expression if there are no enclosing parentheses).
+
+So, `sum` and `product` can also be expressed like this:
+
+      def sum(xs: List[Int])     = (0 :: xs) reduceLeft (_ + _)
+      def product(xs: List[Int]) = (1 :: xs) reduceLeft (_ * _)
+
+FoldLeft
+========
+
+The function `reduceLeft` is defined in terms of a more general function, `foldLeft`.
+
+`foldLeft` is like `reduceLeft` but takes an \red{accumulator}, `z`, as an additional parameter, which is returned when `foldLeft` is called on an empty list.
+
+      (List(x1, ..., xn) foldLeft z)(op)   =  (...(z op x1) op ... ) op xn
+
+\vspace{1.7cm}
+
+So, `sum` and `product` can also be defined as follows:
+
+      def sum(xs: List[Int])      = (xs foldLeft 0) (_ + _)
+      def product(xs: List[Int])  = (xs foldLeft 1) (_ * _)
+
+Implementations of ReduceLeft and FoldLeft
+==========================================
+
+`foldLeft` and `reduceLeft` can be implemented in class `List` as follows.
+
+      abstract class List[T] { ...
+        def reduceLeft(op: (T, T) => T): T = this match {
+          case Nil     => throw new Error("Nil.reduceLeft")
+          case x :: xs => (xs foldLeft x)(op)
+        }
+        def foldLeft[U](z: U)(op: (U, T) => U): U = this match {
+          case Nil     => z
+          case x :: xs => (xs foldLeft op(z, x))(op)
+        }
+      }
+
+FoldRight and ReduceRight
+=========================
+
+Applications of `foldLeft` and `reduceLeft` unfold on trees that lean to the left.
+
+They have two dual functions, `foldRight` and `reduceRight`, which
+produce trees which lean to the right, i.e.,
+
+      List(x1, ..., x{n-1}, xn) reduceRight op = x1 op ( ... (x{n-1} op xn) ... )
+      (List(x1, ..., xn) foldRight acc)(op)    = x1 op ( ... (xn op acc) ... )
+
+Implementation of FoldRight and ReduceRight
+===========================================
+
+They are defined as follows
+
+      def reduceRight(op: (T, T) => T): T = this match {
+        case Nil => throw new Error("Nil.reduceRight")
+        case x :: Nil => x
+        case x :: xs => op(x, xs.reduceRight(op))
+      }
+      def foldRight[](z: U)(op: (T, U) => U): U = this match {
+        case Nil => z
+        case x :: xs => op(x, (xs foldRight z)(op))
+      }
+
+Difference between FoldLeft and FoldRight
+=========================================
+
+For operators that are associative and commutative, `foldLeft` and `foldRight` are equivalent (even though there may be a difference in efficiency).
+
+But sometimes, only one of the two operators is appropriate.
+
+Exercise
+========
+
+Here is another formulation of `concat`:
+
+      def concat[T](xs: List[T], ys: List[T]): List[T] =
+        (xs foldRight ys) (_ :: _)
+
+Here, it isn't possible to replace `foldRight` by `foldLeft`. Why?
+
+      O     The types would not work out
+      O     The resulting function would not terminate
+      O     The result would be reversed
+
+Back to Reversing Lists
+=======================
+
+We now develop a function for reversing lists which has a linear cost.
+
+The idea is to use the operation `foldLeft`:
+
+      def reverse[T](xs: List[T]): List[T] = (xs foldLeft z?)(op?)
+
+All that remains is to replace the parts `z?` and `op?`.
+
+Let's try to \red{compute} them from examples.
+
+Deduction of Reverse (1)
+========================
+
+To start computing `z?`, let's consider `reverse(Nil)`.
+
+We know `reverse(Nil) == Nil`, so we can compute as follows:
+
+      Nil
+->
+      =   reverse(Nil)                 
+->
+      =   (Nil foldLeft z?)(op)   
+->
+      =   z?
+
+Consequently, `z? = List()`
+
+Deduction of Reverse (2)
+========================
+
+We still need to compute `op?`. To do that let's plug in the next simplest
+list after `Nil` into our equation for `reverse`:
+
+       List(x)
+->
+       =   reverse(List(x))
+->
+       =   (List(x) foldLeft Nil)(op?)
+->
+       =   op?(Nil, x)
+
+Consequently, `op?(Nil, x) = List(x) = x :: List()`.
+
+This suggests to take for `op?` the operator `::` but with its operands swapped.
+
+Deduction of Reverse(3)
+=======================
+
+We thus arrive at the following implementation of `reverse`.
+
+      def reverse[a](xs: List[T]): List[T] =
+        (xs foldLeft List[T]())((xs, x) => x :: xs)
+
+Remark: the type parameter in `List[T]()` is necessary for type inference.
+
+\question: What is the complexity of this implementation of `reverse` ?
+
+
+Exercise
+========
+
+Complete the following definitions of the basic functions `map` and `length` on lists, such that their implementation uses `foldRight`:
+
+      def mapFun[T, U](xs: List[T], f: T => U): List[U] =
+        (xs foldRight List[U]())( ??? )
+
+      def lengthFun[T](xs: List[T]): Int =
+        (xs foldRight 0)( ??? )
+
diff --git a/lectures/progfun1-5-6.md b/lectures/progfun1-5-6.md
new file mode 100644
index 0000000000000000000000000000000000000000..65161b0bf817168aa569e405069e23b536440db4
--- /dev/null
+++ b/lectures/progfun1-5-6.md
@@ -0,0 +1,172 @@
+% Reasoning About Lists
+%
+%
+
+Laws of Concat
+==================
+
+Recall the concatenation operation `++` on lists.
+
+We would like to verify that concatenation is associative, and that it admits the empty list \verb@Nil@ as neutral element to the left and to the right:
+
+       (xs ++ ys) ++ zs  =  xs ++ (ys ++ zs)
+              xs ++ Nil  =  xs
+              Nil ++ xs  =  xs
+
+\red{Q}: How can we prove properties like these?
+->
+\red{A}: By \red{structural induction} on lists.
+
+
+Reminder: Natural Induction
+===========================
+
+Recall the principle of proof by \red{natural induction}:
+
+To show a property $\btt P(n)$ for all the integers $\btt n \geq b$,
+
+ - Show that we have $\btt P(b)$ (\red{base case}),
+ - for all integers $\btt n \geq b$ show the \red{induction step}:
+\begin{quote}
+     if one has $\btt P(n)$, then one also has $\btt P(n+1)$.
+\end{quote}
+
+Example
+=======
+
+Given:
+
+     def factorial(n: Int): Int =
+       if n == 0 then 1         // 1st clause
+       else n * factorial(n-1)  // 2nd clause
+
+Show that, for all `n >= 4`
+
+      factorial(n) >= power(2, n)
+
+Base Case
+=========
+
+\BaseCase{{\tt 4}}
+
+This case is established by simple calculations:
+
+      factorial(4) = 24 >= 16 = power(2, 4)
+
+Induction Step
+==============
+
+\InductionStep{\tt n+1}
+
+We have for `n >= 4`:
+
+      factorial(n + 1)
+->
+      >=  (n + 1) * factorial(n)   // by 2nd clause in factorial
+->
+      >   2 * factorial(n)         // by calculating
+->
+      >=  2 * power(2, n)          // by induction hypothesis
+->
+      =   power(2, n + 1)          // by definition of power
+
+Referential Transparency
+========================
+
+Note that a proof can freely apply reduction steps as equalities to some
+part of a term.
+
+That works because pure functional programs don't have side effects;
+so that a term is equivalent to the term to which it reduces.
+
+This principle is called \red{referential transparency}.
+
+
+Structural Induction
+====================
+
+The principle of structural induction is analogous to natural induction:
+
+To prove a property $\btt P(xs)$ for all lists $\btt xs$,
+
+ -  show that $\btt P(Nil)$ holds (\red{base case}),
+ -  for a list $\btt xs$ and some element $\btt x$, show the \red{induction step}:
+\begin{quote}
+  if $\btt P(xs)$ holds, then $\btt P(x :: xs)$ also holds.
+\end{quote}
+
+Example
+=======
+
+Let's show that, for lists `xs`, `ys`, `zs`:
+
+       (xs ++ ys) ++ zs  =  xs ++ (ys ++ zs)
+
+To do this, use structural induction on `xs`. From the previous implementation of `concat`,
+
+      def concat[T](xs: List[T], ys: List[T]) = xs match {
+        case List() => ys
+        case x :: xs1 => x :: concat(xs1, ys)
+      }
+
+distill two _defining clauses_ of `++`:
+
+             Nil ++ ys  =  ys                   // 1st clause
+      (x :: xs1) ++ ys  =  x :: (xs1 ++ ys)     // 2nd clause
+
+
+Base Case
+=========
+
+\BaseCase{\tt Nil}
+
+For the left-hand side we have:
+
+      (Nil ++ ys) ++ zs
+->
+      =   ys ++ zs        // by 1st clause of ++
+->
+For the right-hand side, we have:
+
+      Nil ++ (ys ++ zs)
+->
+      =   ys ++ zs        // by 1st clause of ++
+
+This case is therefore established.
+
+Induction Step: LHS
+===================
+
+\InductionStep{\tt x :: xs}
+
+For the left-hand side, we have:
+
+      ((x :: xs) ++ ys) ++ zs
+->
+      =   (x :: (xs ++ ys)) ++ zs    // by 2nd clause of ++
+->
+      =   x :: ((xs ++ ys) ++ zs)    // by 2nd clause of ++
+->
+      =   x :: (xs ++ (ys ++ zs))    // by induction hypothesis
+
+Induction Step: RHS
+===================
+
+For the right hand side we have:
+
+      (x :: xs) ++ (ys ++ zs)
+->
+      =   x :: (xs ++ (ys ++ zs))    // by 2nd clause of ++
+
+So this case (and with it, the property) is established.
+
+Exercise
+========
+
+Show by induction on `xs` that `xs ++ Nil  =  xs`.
+
+How many equations do you need for the inductive step?
+
+     O          2
+     O          3
+     O          4
\ No newline at end of file
diff --git a/lectures/progfun1-5-7.md b/lectures/progfun1-5-7.md
new file mode 100644
index 0000000000000000000000000000000000000000..68687b4d4cd1d13c89e9acbe2d74345c662cc7a7
--- /dev/null
+++ b/lectures/progfun1-5-7.md
@@ -0,0 +1,102 @@
+% A Larger Equational Proof on Lists
+%
+%
+
+A Law of Reverse
+================
+
+For a more difficult example, let's consider the `reverse` function.
+
+We pick its inefficient definition, because its more amenable to equational proofs:
+
+            Nil.reverse  =  Nil                     // 1st clause
+      (x :: xs).reverse  =  xs.reverse ++ List(x)   // 2nd clause
+
+We'd like to prove the following proposition
+
+      xs.reverse.reverse  =  xs
+
+Proof
+=====
+
+By induction on \verb@xs@. The base case is easy:
+
+      Nil.reverse.reverse
+      =   Nil.reverse           // by 1st clause of reverse
+      =   Nil                   // by 1st clause of reverse
+->
+For the induction step, let's try:
+
+      (x :: xs).reverse.reverse
+      =   (xs.reverse ++ List(x)).reverse // by 2nd clause of reverse
+->
+We can't do anything more with this expression, therefore we turn to the right-hand side:
+
+      x :: xs
+      =   x :: xs.reverse.reverse         // by induction hypothesis
+
+Both sides are simplified in different expressions.
+
+To Do
+=====
+
+We still need to show:
+
+      (xs.reverse ++ List(x)).reverse  =  x :: xs.reverse.reverse
+
+Trying to prove it directly by induction doesn't work.
+
+We must instead try to \red{generalize} the equation. For \red{any} list `ys`,
+
+      (ys ++ List(x)).reverse  =  x :: ys.reverse
+
+This equation can be proved by a second induction argument on `ys`.
+
+
+Auxiliary Equation, Base Case
+=============================
+
+      (Nil ++ List(x)).reverse       // to show: =  x :: Nil.reverse
+->
+      =   List(x).reverse            // by 1st clause of ++
+->
+      =   (x :: Nil).reverse         // by definition of List
+->
+      =   Nil ++ (x :: Nil)          // by 2nd clause of reverse
+->
+      =   x :: Nil                   // by 1st clause of ++
+->
+      =   x :: Nil.reverse           // by 1st clause of reverse
+
+
+
+Auxiliary Equation, Inductive Step
+==================================
+
+      ((y :: ys) ++ List(x)).reverse            // to show: =  x :: (y :: ys).reverse
+->
+      =   (y :: (ys ++ List(x))).reverse        // by 2nd clause of ++
+->
+      =   (ys ++ List(x)).reverse ++ List(y)    // by 2nd clause of reverse
+->
+      =   (x :: ys.reverse) ++ List(y)          // by the induction hypothesis
+->
+      =   x :: (ys.reverse ++ List(y))          // by 1st clause of ++
+->
+      =   x :: (y :: ys).reverse                // by 2nd clause of reverse
+
+This establishes the auxiliary equation, and with it the main proposition.
+
+Exercise (Open-Ended, Harder)
+=============================
+
+Prove the following distribution law for `map` over concatenation.
+
+For any lists `xs`, `ys`, function `f`:
+
+      (xs ++ ys) map f  =  (xs map f) ++ (ys map f)
+
+You will need the clauses of ++ as well as the following clauses for `map`:
+
+            Nil map f  =  Nil
+      (x :: xs) map f  =  f(x) :: (xs map f)
diff --git a/lectures/progfun1-6-1.md b/lectures/progfun1-6-1.md
new file mode 100644
index 0000000000000000000000000000000000000000..5e2eb4ab24bcb7d7b90f2ceea00f8fbd133e91c8
--- /dev/null
+++ b/lectures/progfun1-6-1.md
@@ -0,0 +1,154 @@
+% Other Collections
+%
+%
+Other Sequences
+===============
+
+We have seen that lists are _linear_: Access to the first element is
+much faster than access to the middle or end of a list.
+
+The Scala library also defines an alternative sequence implementation, `Vector`.
+
+This one has more evenly balanced access patterns than `List`.
+
+
+
+Operations on Vectors
+=====================
+
+Vectors are created analogously to lists:
+
+      val nums = Vector(1, 2, 3, -88)
+      val people = Vector("Bob", "James", "Peter")
+
+They support the same operations as lists, with the exception of `::`
+
+Instead of `x :: xs`, there is
+
+\begin{tabular}{lp{8.7cm}}
+   \verb`  x +: xs`  & Create a new vector with leading element \verb`x`, followed by all elements of \verb`xs`.
+\\ \verb`  xs :+ x`  & Create a new vector with trailing element \verb`x`, preceded by all elements of \verb`xs`.
+\end{tabular}
+(Note that the `:` always points to the sequence.)
+
+
+Collection Hierarchy
+====================
+
+A common base class of `List` and `Vector` is `Seq`, the class of all _sequences_. 
+
+`Seq` itself is a subclass of `Iterable`. 
+
+
+
+
+
+Arrays and Strings
+==================
+
+Arrays and Strings support the same operations as `Seq` and can
+implicitly be converted to sequences where needed.
+
+(They cannot be subclasses of `Seq` because they come from Java)
+
+      val xs: Array[Int] = Array(1, 2, 3)
+      xs map (x => 2 * x)
+
+      val ys: String = "Hello world!"
+      ys filter (_.isUpper)
+
+Ranges
+======
+
+Another simple kind of sequence is the _range_.
+
+It represents a sequence of evenly spaced integers.
+
+Three operators:
+
+`to` (inclusive), `until` (exclusive), `by` (to
+determine step value):
+
+      val r: Range = 1 until 5
+      val s: Range = 1 to 5
+      1 to 10 by 3
+      6 to 1 by -2
+
+Ranges a represented as single objects with three fields: 
+lower bound, upper bound, step value.
+
+Some more Sequence Operations:
+==============================
+
+\begin{tabular}{lp{8.7cm}}
+   \verb`  xs exists p    `  & \verb`true` if there is an element \verb`x` of \verb`xs` such that \verb`p(x)` holds, \verb`false` otherwise.
+\\ \verb`  xs forall p    `  & \verb`true` if \verb`p(x)` holds for all elements \verb`x` of \verb`xs`, \verb`false` otherwise.
+\\ \verb`  xs zip ys      `  & A sequence of pairs drawn from corresponding elements of sequences \verb`xs` and \verb`ys`.
+\\ \verb`  xs.unzip       `  & Splits a sequence of pairs \verb`xs` into two sequences consisting of the first, respectively second halves of all pairs.
+\\ \verb`  xs.flatMap f   `  & Applies collection-valued function \verb`f` to all elements of \verb`xs` and concatenates the results
+\\ \verb`  xs.sum         `  & The sum of all elements of this numeric collection.
+\\ \verb`  xs.product     `  & The product of all elements of this numeric collection
+\\ \verb`  xs.max         `  & The maximum of all elements of this collection (an \verb`Ordering` must exist)
+\\ \verb`  xs.min         `  & The minimum of all elements of this collection
+\end{tabular}
+
+Example: Combinations
+=====================
+
+To list all combinations of numbers `x` and `y` where `x` is drawn from `1..M` and `y` is drawn from `1..N`:
+
+      (1 to M) flatMap (x => 
+
+Example: Combinations
+=====================
+
+To list all combinations of numbers `x` and `y` where `x` is drawn from `1..M` and `y` is drawn from `1..N`:
+
+      (1 to M) flatMap (x => (1 to N) map (y => (x, y)))
+
+Example: Scalar Product
+=======================
+
+To compute the scalar product of two vectors:
+
+      def scalarProduct(xs: Vector[Double], ys: Vector[Double]): Double =
+        (xs zip ys).map(xy => xy._1 * xy._2).sum
+->
+An alternative way to write this is with a \red{pattern matching function value}.
+
+      def scalarProduct(xs: Vector[Double], ys: Vector[Double]): Double =
+        (xs zip ys).map{ case (x, y) => x * y }.sum
+
+Generally, the function value
+
+      { case p1 => e1 ... case pn => en }
+
+is equivalent to 
+
+      x => x match { case p1 => e1 ... case pn => en }
+
+Exercise:
+=========
+
+A number `n` is \red{prime} if the only divisors of `n` are `1` and `n` itself.
+
+What is a high-level way to write a test for primality of numbers? For
+once, value conciseness over efficiency.
+
+      def isPrime(n: Int): Boolean = ???
+
+
+Exercise:
+=========
+
+A number `n` is \red{prime} if the only divisors of `n` are `1` and `n` itself.
+
+What is a high-level way to write a test for primality of numbers? For
+once, value conciseness over efficiency.
+
+      def isPrime(n: Int): Boolean = 
+
+\quiz
+
+
+
diff --git a/lectures/progfun1-6-2.md b/lectures/progfun1-6-2.md
new file mode 100644
index 0000000000000000000000000000000000000000..2fbe678689582d8453fc62ae8c15989e2b9d4b7b
--- /dev/null
+++ b/lectures/progfun1-6-2.md
@@ -0,0 +1,155 @@
+% Combinatorial Search and For-Expressions
+%
+%
+Handling Nested Sequences
+=========================
+
+We can extend the usage of higher order functions on sequences to many
+calculations which are usually expressed using nested loops.
+
+\example: Given a positive integer `n`, find all pairs of positive integers `i` and `j`, with `1 <= j < i < n` such that `i + j` is prime.
+
+For example, if `n = 7`, the sought pairs are
+
+$$\ba{c|lllllll}
+i     & 2 & 3 & 4 & 4 & 5 & 6 & 6\\
+j     & 1 & 2 & 1 & 3 & 2 & 1 & 5\\ \hline
+i + j & 3 & 5 & 5 & 7 & 7 & 7 & 11
+\ea$$
+
+Algorithm
+=========
+
+A natural way to do this is to:
+
+- Generate the sequence of all pairs of integers `(i, j)` such that `1 <= j < i < n`.
+- Filter the pairs for which `i + j` is prime.
+
+One natural way to generate the sequence of pairs is to:
+
+- Generate all the integers `i` between `1` and `n` (excluded).
+- For each integer `i`, generate the list of pairs `(i, 1), ..., (i, i-1)`.
+
+This can be achieved by combining `until` and `map`:
+
+      (1 until n) map (i =>
+        (1 until i) map (j => (i, j)))
+
+
+Generate Pairs
+==============
+
+The previous step gave a sequence of sequences, let's call it `xss`.
+
+We can combine all the sub-sequences using `foldRight` with `++`:
+
+      (xss foldRight Seq[Int]())(_ ++ _)
+
+Or, equivalently, we use the built-in method `flatten`
+
+      xss.flatten
+
+This gives:
+
+      ((1 until n) map (i =>
+        (1 until i) map (j => (i, j)))).flatten
+
+Generate Pairs (2)
+==================
+
+Here's a useful law: 
+
+     xs flatMap f  =  (xs map f).flatten
+
+Hence, the above expression can be simplified to
+
+    (1 until n) flatMap (i =>
+        (1 until i) map (j => (i, j)))
+
+
+Assembling the pieces
+=====================
+
+By reassembling the pieces, we obtain the following expression:
+
+    (1 until n) flatMap (i =>
+       (1 until i) map (j => (i, j))) filter ( pair =>
+          isPrime(pair._1 + pair._2))
+
+This works, but makes most people's head hurt.
+
+Is there a simpler way?
+
+
+For-Expressions
+===============
+
+Higher-order functions such as `map`, `flatMap` or `filter` provide powerful constructs for manipulating lists.
+
+But sometimes the level of abstraction required by these function make the program difficult to understand.
+
+In this case, Scala's `for` expression notation can help.
+
+For-Expression Example
+======================
+
+Let `persons` be a list of elements of class `Person`, with fields `name` and `age`. 
+
+      case class Person(name: String, age: Int)
+
+To obtain the names of persons over 20 years old, you can write: 
+
+      for ( p <- persons if p.age > 20 ) yield p.name
+
+which is equivalent to:
+
+      persons filter (p => p.age > 20) map (p => p.name)
+
+The for-expression is similar to loops in imperative languages, except
+that it builds a list of the results of all iterations.
+
+Syntax of For
+=============
+
+A for-expression is of the form
+
+      for ( s ) yield e
+
+where `s` is a sequence of \red{generators} and \red{filters},
+and `e` is an expression whose value is returned by an iteration. 
+
+- A \red{generator} is of the form `p <- e`, 
+  where `p` is a pattern and `e` an expression whose value is a collection. 
+- A \red{filter} is of the form `if f` where `f` is a boolean expression.
+- The sequence must start with a generator.
+- If there are several generators in the sequence, the last generators vary faster than the first.
+
+Instead of `( s )`, braces `{ s }` can also be used, and then the
+sequence of generators and filters can be written on multiple lines
+without requiring semicolons.
+
+Use of For
+==========
+
+Here are two examples which were previously solved with higher-order functions:
+
+Given a positive integer `n`, find all the pairs of positive integers
+`(i, j)` such that `1 <= j < i < n`, and `i + j` is prime.
+
+       for {
+         i <- 1 until n
+         j <- 1 until i
+         if isPrime(i + j)
+       } yield (i, j)
+
+
+Exercise
+========
+
+Write a version of `scalarProduct` (see last session) that makes use of
+a `for`:
+
+      def scalarProduct(xs: List[Double], ys: List[Double]) : Double =
+->
+        (for ((x, y) <- xs zip ys) yield x * y).sum
+\quiz
diff --git a/lectures/progfun1-6-3.md b/lectures/progfun1-6-3.md
new file mode 100644
index 0000000000000000000000000000000000000000..826285276a86787614606223ab895a06f56db69d
--- /dev/null
+++ b/lectures/progfun1-6-3.md
@@ -0,0 +1,87 @@
+% Combinatorial Search Example
+%
+%
+Sets
+====
+
+Sets are another basic abstraction in the Scala collections.
+
+A set is written analogously to a sequence:
+
+      val fruit = Set("apple", "banana", "pear")
+      val s = (1 to 6).toSet
+
+Most operations on sequences are also available on sets:
+
+      s.map(_ + 2)
+      fruit.filter(_.startsWith == "app")
+      s.nonEmpty
+
+(see `Iterable`s Scaladoc for a list of all supported operations)
+
+Sets vs Sequences
+=================
+
+The principal differences between sets and sequences are:
+
+$1.$ Sets are unordered; the elements of a set do not have a predefined order in which they appear in the set
+
+$2.$ sets do not have duplicate elements:
+
+      s.map(_ / 2  )      // Set(2, 0, 3, 1)
+
+$3.$ The fundamental operation on sets is `contains`:
+
+      s.contains(5)       // true
+
+Example: N-Queens
+=================
+
+The eight queens problem is to place eight queens on a chessboard so that no queen is threatened by another.
+
+- In other words, there can't be two queens in the same row, column, or diagonal.
+
+We now develop a solution for a chessboard of any size, not just 8.
+
+One way to solve the problem is to place a queen on each row.
+
+Once we have placed `k - 1` queens, one must place the `k`th queen in a column where it's not ``in check" with any other queen on the board.
+
+Algorithm
+=========
+
+We can solve this problem with a recursive algorithm:
+
+- Suppose that we have already generated all the solutions consisting of placing `k-1` queens on a board of size `n`.
+- Each solution is represented by a list (of length `k-1`) containing the numbers of columns (between `0` and `n-1`).
+- The column number of the queen in the `k-1`th row comes first in the list, followed by the column number of the queen in row `k-2`, etc.
+- The solution set is thus represented as a set of lists, with one element for each solution.
+- Now, to place the `k`th queen, we generate all possible extensions of each solution preceded by a new queen:
+
+Implementation
+==============
+
+      def queens(n: Int) =
+        def placeQueens(k: Int): Set[List[Int]] =
+          if k == 0 Set(List())
+          else
+            for
+              queens <- placeQueens(k - 1)
+              col <- 0 until n
+              if isSafe(col, queens)
+            yield
+              col :: queens
+        placeQueens(n)
+
+Exercise
+========
+
+Write a function
+
+       def isSafe(col: Int, queens: List[Int]): Boolean
+
+which tests if a queen placed in an indicated column `col` is secure amongst the other placed queens.
+
+It is assumed that the new queen is placed in the next available row after the other placed queens (in other words: in row `queens.length`).
+
+\quiz
diff --git a/lectures/progfun1-6-4.md b/lectures/progfun1-6-4.md
new file mode 100644
index 0000000000000000000000000000000000000000..dfcc4c813042d9ef75c2b016a6761a25bb5341b2
--- /dev/null
+++ b/lectures/progfun1-6-4.md
@@ -0,0 +1,193 @@
+% Maps
+%
+%
+Map
+===
+
+Another fundamental collection type is the \red{map}.
+
+A map of type `Map[Key, Value]` is a data structure
+that associates keys of type `Key` with values of type `Value`.
+
+Examples:
+
+      val romanNumerals = Map("I" -> 1, "V" -> 5, "X" -> 10)
+      val capitalOfCountry = Map("US" -> "Washington", "Switzerland" -> "Bern")
+
+Maps are Iterables
+==================
+
+Class `Map[Key, Value]` extends the collection type \newline
+`Iterable[(Key, Value)]`.
+
+Therefore, maps support the same collection operations as other iterables do. Example:
+
+      val countryOfCapital = capitalOfCountry map {
+        case(x, y) => (y, x)
+      }                      // Map("Washington" -> "US", "Bern" -> "Switzerland")
+
+Note that maps extend iterables of key/value _pairs_.
+
+In fact, the syntax `key -> value` is just an alternative way to write the pair `(key, value)`.
+
+
+Maps are Functions
+==================
+
+Class `Map[Key, Value]` also extends the function type `Key => Value`, so maps can be used everywhere functions can.
+
+In particular, maps can be applied to key arguments:
+
+      capitalOfCountry("US")         // "Washington"
+
+
+Querying Map
+============
+
+Applying a map to a non-existing key gives an error:
+
+      capitalOfCountry("Andorra")
+        // java.util.NoSuchElementException: key not found: Andorra
+
+To query a map without knowing beforehand whether it contains a given key, you can use the `get` operation:
+
+      capitalOfCountry get "US"      // Some("Washington")
+      capitalOfCountry get "Andorra" // None
+
+The result of a `get` operation is an `Option` value.
+
+The `Option` Type
+=================
+
+The `Option` type is defined as:
+
+      trait Option[+A]
+      case class Some[+A](value: A) extends Option[A]
+      object None extends Option[Nothing]
+
+The expression `map get key` returns
+
+- `None` \gap if `map` does not contain the given `key`,
+- `Some(x)` \ \ if `map` associates the given `key` with the value `x`.
+
+Decomposing Option
+==================
+
+Since options are defined as case classes, they can be decomposed
+using pattern matching:
+
+      def showCapital(country: String) = capitalOfCountry.get(country) match {
+        case Some(capital) => capital
+        case None => "missing data"
+      }
+
+      showCapital("US")      // "Washington"
+      showCapital("Andorra") // "missing data"
+
+Options also support quite a few operations of the other collections.
+
+I invite you to try them out!
+
+Sorted and GroupBy
+==================
+
+Two useful operation of SQL queries in addition to for-expressions are
+`groupBy` and `orderBy`.
+
+`orderBy` on a collection can be expressed by `sortWith` and `sorted`.
+
+      val fruit = List("apple", "pear", "orange", "pineapple")
+      fruit sortWith (_.length < _.length)  // List("pear", "apple", "orange", "pineapple")
+      fruit.sorted      // List("apple", "orange", "pear", "pineapple")
+
+`groupBy` is available on Scala collections. It partitions a collection
+into a map of collections according to a _discriminator function_ `f`.
+
+\example:
+
+      fruit groupBy (_.head)  //> Map(p -> List(pear, pineapple),
+                              //|     a -> List(apple),
+                              //|     o -> List(orange))
+Map Example
+===========
+
+A polynomial can be seen as a map from exponents to coefficients.
+
+For instance, $\btt x^3 - 2x + 5$ can be represented with the map.
+
+      Map(0 -> 5, 1 -> -2, 3 -> 1)
+
+Based on this observation, let's design a class `Polynom` that represents polynomials as maps.
+
+Default Values
+==============
+
+So far, maps were \red{partial functions}: Applying a map to a key
+value in `map(key)` could lead to an exception, if the key was not stored
+in the map.
+
+There is an operation `withDefaultValue` that turns a map into a
+total function:
+
+      val cap1 = capitalOfCountry withDefaultValue "<unknown>"
+      cap1("Andorra")             // "<unknown>"
+
+Variable Length Argument Lists
+==============================
+
+It's quite inconvenient to have to write
+
+      Polynom(Map(1 -> 2.0, 3 -> 4.0, 5 -> 6.2))
+
+Can one do without the `Map(...)`?
+
+Problem: The number of `key -> value` pairs passed to `Map` can vary.
+->
+We can accommodate this pattern using a \red{repeated parameter}:
+
+       def Polynom(bindings: (Int, Double)*) =
+         new Polynom(bindings.toMap withDefaultValue 0)
+
+       Polynom(1 -> 2.0, 3 -> 4.0, 5 -> 6.2)
+
+Inside the `Polynom` function, `bindings` is seen as a
+`Seq[(Int, Double)]`.
+
+Final Implementation of Polynom
+===============================
+
+    class Poly(terms0: Map[Int, Double]) {
+      def this(bindings: (Int, Double)*) = this(bindings.toMap)
+      val terms = terms0 withDefaultValue 0.0
+      def + (other: Poly) = new Poly(terms ++ (other.terms map adjust))
+      def adjust(term: (Int, Double)): (Int, Double) = {
+        val (exp, coeff) = term
+        exp -> (coeff + terms(exp))
+      }
+
+    override def toString =
+      (for ((exp, coeff) <- terms.toList.sorted.reverse)
+        yield coeff+"x^"+exp) mkString " + "
+    }
+
+Exercise
+========
+
+The `+` operation on `Poly` used map concatenation with `++`.
+Design another version of `+` in terms of `foldLeft`:
+
+    def + (other: Poly) =
+      new Poly((other.terms foldLeft ???)(addTerm)
+
+    def addTerm(terms: Map[Int, Double], term: (Int, Double)) =
+      ???
+
+Which of the two versions do you believe is more efficient?
+
+      O        The version using ++
+      O        The version using foldLeft
+
+->
+
+\quiz
+
diff --git a/lectures/progfun1-6-5.md b/lectures/progfun1-6-5.md
new file mode 100644
index 0000000000000000000000000000000000000000..1b3d92a4eaba7af58d146cbc5b9153890151d54c
--- /dev/null
+++ b/lectures/progfun1-6-5.md
@@ -0,0 +1,51 @@
+% Putting the Pieces Together
+%
+%
+Task
+====
+
+Phone keys have mnemonics assigned to them.
+
+      val mnemonics = Map(
+            '2' -> "ABC", '3' -> "DEF", '4' -> "GHI", '5' -> "JKL", 
+            '6' -> "MNO", '7' -> "PQRS", '8' -> "TUV", '9' -> "WXYZ")
+
+Assume you are given a dictionary `words` as a list of words. 
+
+Design a method `translate` such that
+
+       translate(phoneNumber)
+
+produces all phrases of words 
+that can serve as mnemonics for the phone number.
+
+\example: The phone number "7225247386" should have the mnemonic
+`Scala is fun` as one element of the set of solution phrases.
+
+Background
+==========
+
+This example was taken from:
+\begin{quote}
+Lutz Prechelt: An Empirical Comparison of Seven Programming Languages. IEEE Computer 33(10): 23-29 (2000) 
+\end{quote}
+
+Tested with Tcl, Python, Perl, Rexx, Java, C++, C.
+
+Code size medians: 
+
+- 100 loc  for scripting languages
+- 200-300 loc for the others
+
+The Future?
+===========
+
+Scala's immutable collections are:
+
+- _easy to use_: few steps to do the job.
+- _concise_: one word replaces a whole loop.
+- _safe_: type checker is really good at catching errors.
+- _fast_: collection ops are tuned, can be parallelized.
+- _universal_: one vocabulary to work on all kinds of collections.
+
+This makes them a very attractive tool for software development
diff --git a/lectures/progfun2-1-1.md b/lectures/progfun2-1-1.md
new file mode 100644
index 0000000000000000000000000000000000000000..333e92de9edde53381210044ee3c48036f9a356e
--- /dev/null
+++ b/lectures/progfun2-1-1.md
@@ -0,0 +1,118 @@
+% Queries with For
+%
+%
+Queries with `for`
+==================
+
+The for notation is essentially equivalent to the common operations of
+query languages for databases.
+
+\example: Suppose that we have a database `books`, represented as a list of books. 
+
+      case class Book(title: String, authors: List[String])
+
+A Mini-Database
+===============
+
+      val books: List[Book] = List(
+        Book(title   = "Structure and Interpretation of Computer Programs",
+             authors = List("Abelson, Harald", "Sussman, Gerald J.")),
+        Book(title   = "Introduction to Functional Programming",
+             authors = List("Bird, Richard", "Wadler, Phil")),
+        Book(title   = "Effective Java",
+             authors = List("Bloch, Joshua")),
+        Book(title   = "Java Puzzlers",
+             authors = List("Bloch, Joshua", "Gafter, Neal")),
+        Book(title   = "Programming in Scala",
+             authors = List("Odersky, Martin", "Spoon, Lex", "Venners, Bill")))
+
+Some Queries
+============
+
+To find the titles of books whose author's name is "Bird":
+
+      for (b <- books; a <- b.authors if a startsWith "Bird,")
+      yield b.title
+
+To find all the books which have the word ``Program'' in the title:
+
+      for (b <- books if b.title indexOf "Program" >= 0)
+      yield b.title
+
+Another Query
+=============
+
+To find the names of all authors who have written at least two books present in the database.
+
+      for {
+        b1 <- books
+        b2 <- books
+        if b1 != b2
+        a1 <- b1.authors
+        a2 <- b2.authors
+        if a1 == a2
+      } yield a1
+->
+Why do solutions show up twice?
+
+How can we avoid this?
+
+Modified Query
+==============
+
+To find the names of all authors who have written at least two books present in the database.
+
+      for {
+        b1 <- books
+        b2 <- books
+        if b1.title < b2.title 
+        a1 <- b1.authors
+        a2 <- b2.authors
+        if a1 == a2
+      } yield a1
+
+Problem
+=======
+
+What happens if an author has published three books?
+
+      O    The author is printed once
+      O    The author is printed twice
+      O    The author is printed three times
+      O    The author is not printed at all
+
+->
+\quiz
+
+Modified Query (2)
+==================
+
+\red{Solution}: We must remove duplicate authors who are in the results list twice.
+
+This is achieved using the `distinct` method on sequences:
+
+      { for {
+          b1 <- books
+          b2 <- books
+          if b1.title < b2.title 
+          a1 <- b1.authors
+          a2 <- b2.authors
+          if a1 == a2
+        } yield a1
+      }.distinct
+
+Modified Query
+==============
+
+\red{Better alternative}: Compute with sets instead of sequences:
+
+      val bookSet = books.toSet
+      for {
+        b1 <- bookSet
+        b2 <- bookSet
+        if b1 != b2
+        a1 <- b1.authors
+        a2 <- b2.authors
+        if a1 == a2
+      } yield a1
+
diff --git a/lectures/progfun2-1-2.md b/lectures/progfun2-1-2.md
new file mode 100644
index 0000000000000000000000000000000000000000..0df769a1c533473da770172ba8c91e1fdc39db50
--- /dev/null
+++ b/lectures/progfun2-1-2.md
@@ -0,0 +1,123 @@
+% Translation of For
+%
+%
+For-Expressions and Higher-Order Functions
+==========================================
+
+The syntax of `for` is closely related to the higher-order functions `map`, `flatMap` and `filter`.
+
+First of all, these functions can all be defined in terms of `for`:
+
+      def mapFun[T, U](xs: List[T], f: T => U): List[U] = 
+        for (x <- xs) yield f(x)
+
+      def flatMap[T, U](xs: List[T], f: T => Iterable[U]): List[U] = 
+        for (x <- xs; y <- f(x)) yield y
+
+      def filter[T](xs: List[T], p: T => Boolean): List[T] = 
+        for (x <- xs if p(x)) yield x
+
+Translation of For (1)
+======================
+
+In reality, the Scala compiler expresses for-expressions in terms of `map`, `flatMap` and a lazy variant of `filter`.
+
+Here is the translation scheme used by the compiler (we limit ourselves here to simple variables in generators)
+
+$1.$ A simple for-expression
+
+      for (x <- e1) yield e2
+
+is translated to
+
+      e1.map(x => e2)
+
+Translation of For (2)
+======================
+
+$2.$ A for-expression
+
+      for (x <- e1 if f; s) yield e2
+
+where `f` is a filter and `s` is a (potentially empty) sequence of generators and filters, is translated to
+
+      for (x <- e1.withFilter(x => f); s) yield e2
+
+(and the translation continues with the new expression)
+
+You can think of `withFilter` as a variant of `filter` that does not
+produce an intermediate list, but instead filters the following `map` or `flatMap` function application.
+
+Translation of For (3)
+======================
+
+$3.$ A for-expression
+
+      for (x <- e1; y <- e2; s) yield e3
+
+where `s` is a (potentially empty) sequence of generators and filters, is translated into
+
+      e1.flatMap(x => for (y <- e2; s) yield e3)
+
+(and the translation continues with the new expression)
+
+Example
+=======
+
+Take the for-expression that computed pairs whose sum is prime:
+
+       for {
+         i <- 1 until n
+         j <- 1 until i
+         if isPrime(i + j)
+       } yield (i, j)
+
+Applying the translation scheme to this expression gives:
+
+       (1 until n).flatMap(i => 
+         (1 until i).withFilter(j => isPrime(i+j))
+          .map(j => (i, j)))
+
+This is almost exactly the expression which we came up with first!
+
+Exercise
+========
+
+Translate
+
+      for (b <- books; a <- b.authors if a startsWith "Bird") 
+      yield b.title
+
+into higher-order functions.
+
+->
+\quiz
+
+
+Generalization of `for`
+=======================
+
+Interestingly, the translation of for is not limited to lists or
+sequences, or even collections; 
+
+It is based solely on the presence of
+the methods `map`, `flatMap` and `withFilter`.
+
+This lets you use the for syntax for your own types as well -- you must only define `map`, `flatMap` and `withFilter` for these types.
+
+There are many types for which this is useful: arrays, iterators, databases, XML data, optional values, parsers, etc.
+
+For and Databases
+=================
+
+For example, `books` might not be a  list, but a database stored on some server.
+
+As long as the client interface to the database defines the methods `map`, `flatMap` and `withFilter`, we can use the `for` syntax for querying the database.
+
+This is the basis of the Scala data base connection frameworks
+ScalaQuery and Slick.
+
+Similar ideas underly Microsoft's LINQ.
+
+
+
diff --git a/lectures/progfun2-1-3.md b/lectures/progfun2-1-3.md
new file mode 100644
index 0000000000000000000000000000000000000000..d503314a5530292ad8b43ed8fc0e446b28257924
--- /dev/null
+++ b/lectures/progfun2-1-3.md
@@ -0,0 +1,272 @@
+% Functional Random Generators
+% Martin Odersky
+%
+
+Other Uses of For-Expressions
+=============================
+
+Operations of sets, or databases, or options.
+
+\red{Question:} Are for-expressions tied to collections?
+
+\red{Answer:} No! All that is required is some interpretation of
+`map`, `flatMap` and `withFilter`.
+
+There are many domains outside collections that afford such an
+interpretation.
+
+Example: random value generators.
+
+Random Values
+=============
+
+You know about random numbers:
+
+      import java.util.Random
+      val rand = new Random
+      rand.nextInt()
+
+Question: What is a systematic way to get random values for other domains, such as
+
+ - booleans, strings, pairs and tuples, lists, sets, trees
+
+?
+
+Generators
+==========
+
+Let's define a trait `Generator[T]` that generates random values of type `T`:
+
+      trait Generator[+T] {
+        def generate: T
+      }
+
+Some instances:
+
+      val integers = new Generator[Int] {
+        val rand = new java.util.Random
+        def generate = rand.nextInt()
+      }
+
+Generators
+==========
+
+Let's define a trait `Generator[T]` that generates random values of type `T`:
+
+      trait Generator[+T] {
+        def generate: T
+      }
+
+Some instances:
+
+      val booleans = new Generator[Boolean] {
+        def generate = integers.generate > 0
+      }
+
+Generators
+==========
+
+Let's define a trait `Generator[T]` that generates random values of type `T`:
+
+      trait Generator[+T] {
+        def generate: T
+      }
+
+Some instances:
+
+      val pairs = new Generator[(Int, Int)] {
+        def generate = (integers.generate, integers.generate)
+      }
+
+Streamlining It
+===============
+
+Can we avoid the `new Generator ... ` boilerplate?
+
+Ideally, we would like to write:
+
+      val booleans = for (x <- integers) yield x > 0
+
+      def pairs[T, U](t: Generator[T], u: Generator[U]) = for {
+        x <- t
+        y <- u
+      } yield (x, y)
+
+What does this expand to?
+
+Streamlining It
+===============
+
+Can we avoid the `new Generator ... ` boilerplate?
+
+Ideally, we would like to write:
+
+      val booleans = integers map (x => x > 0)
+
+      def pairs[T, U](t: Generator[T], u: Generator[U]) =
+        t flatMap (x => u map (y => (x, y)))
+
+Need `map` and `flatMap` for that!
+
+Generator with `map` and `flatMap`
+==================================
+
+Here's a more convenient version of `Generator`:
+
+      trait Generator[+T] {
+        self =>       // an alias for "this".
+
+        def generate: T
+
+        def map[S](f: T => S): Generator[S] = new Generator[S] {
+          def generate = f(self.generate)
+        }
+->
+        def flatMap[S](f: T => Generator[S]): Generator[S] = new Generator[S] {
+          def generate = f(self.generate).generate
+        }
+      }
+
+The `booleans` Generator
+========================
+
+What does this definition resolve to?
+
+      val booleans = for (x <- integers) yield x > 0
+->
+      val booleans = integers map { x => x > 0 }
+->
+      val booleans = new Generator[Boolean] {
+        def generate = (x: Int => x > 0)(integers.generate)
+      }
+->
+      val booleans = new Generator[Boolean] {
+        def generate = integers.generate > 0
+      }
+
+The `pairs` Generator
+=====================
+
+      def pairs[T, U](t: Generator[T], u: Generator[U]) = t flatMap {
+        x => u map { y => (x, y) } }
+->
+      def pairs[T, U](t: Generator[T], u: Generator[U]) = t flatMap {
+        x => new Generator[(T, U)] { def generate = (x, u.generate) } }
+->
+      def pairs[T, U](t: Generator[T], u: Generator[U]) = new Generator[(T, U)] {
+        def generate = (new Generator[(T, U)] {
+          def generate = (t.generate, u.generate)
+        }).generate }
+->
+      def pairs[T, U](t: Generator[T], u: Generator[U]) = new Generator[(T, U)] {
+        def generate = (t.generate, u.generate)
+      }
+
+Generator Examples
+==================
+
+      def single[T](x: T): Generator[T] = new Generator[T] {
+        def generate = x
+      }
+
+      def choose(lo: Int, hi: Int): Generator[Int] =
+        for (x <- integers) yield lo + x % (hi - lo)
+
+      def oneOf[T](xs: T*): Generator[T] =
+        for (idx <- choose(0, xs.length)) yield xs(idx)
+
+A `List` Generator
+==================
+
+A list is either an empty list or a non-empty list.
+
+    def lists: Generator[List[Int]] = for {
+      isEmpty <- booleans
+      list <- if isEmpty then emptyLists else nonEmptyLists
+    } yield list
+->
+    def emptyLists = single(Nil)
+->
+    def nonEmptyLists = for {
+      head <- integers
+      tail <- lists
+    } yield head :: tail
+
+
+A `Tree` Generator
+==================
+
+Can you implement a generator that creates random `Tree` objects?
+
+    trait Tree
+
+    case class Inner(left: Tree, right: Tree) extends Tree
+
+    case class Leaf(x: Int) extends Tree
+
+Hint: a tree is either a leaf or an inner node.
+
+
+Application: Random Testing
+===========================
+
+You know about units tests:
+
+- Come up with some some test inputs to program functions
+  and a \red{postcondition}.
+- The postcondition is a property of the expected result.
+- Verify that the program satisfies the postcondition.
+
+\red{Question:} Can we do without the test inputs?
+
+Yes, by generating random test inputs.
+
+Random Test Function
+====================
+
+Using generators, we can write a random test function:
+
+      def test[T](g: Generator[T], numTimes: Int = 100)
+          (test: T => Boolean): Unit = {
+        for (i <- 0 until numTimes) {
+          val value = g.generate
+          assert(test(value), "test failed for "+value)
+        }
+        println("passed "+numTimes+" tests")
+      }
+
+Random Test Function
+====================
+
+Example usage:
+
+      test(pairs(lists, lists)) {
+        case (xs, ys) => (xs ++ ys).length > xs.length
+      }
+
+\question: Does the above property always hold?
+
+      O       Yes
+      O       No
+
+\quiz
+
+
+ScalaCheck
+==========
+
+Shift in viewpoint: Instead of writing tests, write \red{properties} that are assumed to hold.
+
+This idea is implemented in the `ScalaCheck` tool.
+
+      forAll { (l1: List[Int], l2: List[Int]) =>
+        l1.size + l2.size == (l1 ++ l2).size
+      }
+
+It can be used either stand-alone or as part of ScalaTest.
+
+See ScalaCheck tutorial on the course page.
+
+
+
+
diff --git a/lectures/progfun2-1-4.md b/lectures/progfun2-1-4.md
new file mode 100644
index 0000000000000000000000000000000000000000..0b8390780fdd3e5d0bacd1819056c4b39493cf50
--- /dev/null
+++ b/lectures/progfun2-1-4.md
@@ -0,0 +1,288 @@
+% Monads
+% Martin Odersky
+%
+Monads
+======
+
+Data structures with `map` and `flatMap` seem to be quite common.
+
+In fact there's a name that describes this class of a data structures
+together with some algebraic laws that they should have.
+
+They are called \red{monads}.
+
+What is a Monad?
+================
+
+A monad `M` is a parametric type M[T] with two operations, `flatMap` and `unit`, that
+have to satisfy some laws.
+
+      trait M[T] {
+        def flatMap[U](f: T => M[U]): M[U]
+      }
+
+      def unit[T](x: T): M[T]
+
+In the literature, `flatMap` is more commonly called `bind`.
+
+
+Examples of Monads
+==================
+
+ - `List` is a monad with `unit(x) = List(x)`
+
+ - `Set` is monad with `unit(x) = Set(x)`
+
+ - `Option` is a monad with `unit(x) = Some(x)`
+
+ - `Generator` is a monad with `unit(x) = single(x)`
+
+`flatMap` is an operation on each of these types, whereas `unit` in Scala is different
+for each monad.
+
+
+Monads and map
+==============
+
+`map` can be defined for every monad as a combination of `flatMap` and `unit`:
+
+      m map f  ==  m flatMap (x => unit(f(x)))
+               ==  m flatMap (f andThen unit)
+
+Monad Laws
+==========
+
+To qualify as a monad, a type has to satisfy three laws:
+
+\red{Associativity:}
+
+        m flatMap f flatMap g  ==  m flatMap (x => f(x) flatMap g)
+
+\red{Left unit}
+
+        unit(x) flatMap f  ==  f(x)
+
+\red{Right unit}
+
+        m flatMap unit  ==  m
+
+Checking Monad Laws
+===================
+
+Let's check the monad laws for Option.
+
+Here's `flatMap` for `Option`:
+
+      abstract class Option[+T] {
+
+        def flatMap[U](f: T => Option[U]): Option[U] = this match {
+          case Some(x) => f(x)
+          case None => None
+        }
+      }
+
+Checking the Left Unit Law
+==========================
+
+Need to show: `Some(x) flatMap f  ==  f(x)`
+
+           Some(x) flatMap f
+->
+      ==   Some(x) match {
+             case Some(x) => f(x)
+             case None => None
+           }
+->
+      ==   f(x)
+
+Checking the Right Unit Law
+===========================
+
+Need to show: `opt flatMap Some  ==  opt`
+
+           opt flatMap Some
+->
+      ==   opt match {
+             case Some(x) => Some(x)
+             case None => None
+           }
+->
+      ==   opt
+
+
+Checking the Associative Law
+============================
+
+Need to show: `opt flatMap f flatMap g  ==  opt flatMap (x => f(x) flatMap g)`
+
+           opt flatMap f flatMap g
+->
+      ==   opt match { case Some(x) => f(x) case None => None }
+               match { case Some(y) => g(y) case None => None }
+->
+      ==   opt match {
+             case Some(x) =>
+               f(x) match { case Some(y) => g(y) case None => None }
+             case None =>
+               None match { case Some(y) => g(y) case None => None }
+           }
+
+
+Checking the Associative Law (2)
+================================
+
+      ==   opt match {
+             case Some(x) =>
+               f(x) match { case Some(y) => g(y) case None => None }
+             case None => None
+           }
+->
+      ==   opt match {
+             case Some(x) => f(x) flatMap g
+             case None => None
+           }
+->
+      ==   opt flatMap (x => f(x) flatMap g)
+
+Significance of the Laws for For-Expressions
+============================================
+
+We have seen that monad-typed expressions are typically written as `for` expressions.
+
+What is the significance of the laws with respect to this?
+
+$1.$ Associativity says essentially that one can "inline" nested for expressions:
+
+           for (y <- for (x <- m; y <- f(x)) yield y
+                z <- g(y)) yield z
+
+      ==   for (x <- m;
+                y <- f(x)
+                z <- g(y)) yield z
+
+Significance of the Laws for For-Expressions
+============================================
+
+$2.$ Right unit says:
+
+           for (x <- m) yield x
+
+      ==   m
+
+$3.$ Left unit does not have an analogue for for-expressions.
+
+Another type: Try
+=================
+
+In the later parts of this course we will need a type named `Try`.
+
+`Try` resembles `Option`, but instead of `Some/None` there is a `Success` case with a value and a `Failure` case that contains an exception:
+
+      abstract class Try[+T]
+      case class Success[T](x: T)       extends Try[T]
+      case class Failure(ex: Exception) extends Try[Nothing]
+
+`Try` is used to pass results of computations that can fail with an
+exception between threads and computers.
+
+Creating a `Try`
+================
+
+You can wrap up an arbitrary computation in a `Try`.
+
+      Try(expr)     // gives Success(someValue) or Failure(someException)
+
+Here's an implementation of `Try`:
+
+      object Try {
+        def apply[T](expr: => T): Try[T] =
+          try Success(expr)
+          catch {
+            case NonFatal(ex) => Failure(ex)
+          }
+
+Composing `Try`
+===============
+
+Just like with `Option`, `Try`-valued computations can be composed in for expresssions.
+
+     for {
+       x <- computeX
+       y <- computeY
+     } yield f(x, y)
+
+If `computeX` and `computeY` succeed with results `Success(x)` and `Success(y)`, this will return `Success(f(x, y))`.
+
+If either computation fails with an exception `ex`, this will return `Failure(ex)`.
+
+Definition of `flatMap` and `map` on `Try`
+==========================================
+
+      abstract class Try[T] {
+        def flatMap[U](f: T => Try[U]): Try[U] = this match {
+          case Success(x) => try f(x) catch { case NonFatal(ex) => Failure(ex) }
+          case fail: Failure => fail
+        }
+
+        def map[U](f: T => U): Try[U] = this match {
+          case Success(x) => Try(f(x))
+          case fail: Failure => fail
+        }}
+
+So, for a `Try` value `t`,
+
+      t map f  ==  t flatMap (x => Try(f(x)))
+               ==  t flatMap (f andThen Try)
+
+Exercise
+========
+
+It looks like `Try` might be a monad, with `unit` = `Try`.
+
+Is it?
+
+      O     Yes
+      O     No, the associative law fails
+      O     No, the left unit law fails
+      O     No, the right unit law fails
+      O     No, two or more monad laws fail.
+
+\quiz
+
+Solution
+========
+
+It turns out the left unit law fails.
+
+      Try(expr) flatMap f  !=  f(expr)
+
+Indeed the left-hand side will never raise a non-fatal exception whereas the
+right-hand side will raise any exception thrown by `expr` or `f`.
+
+Hence, `Try` trades one monad law for another law which is more useful in this context:
+
+\begin{quote}\it
+An expression composed from `Try`, `map`, `flatMap` will never throw a
+non-fatal exception.
+\end{quote}
+
+Call this the "bullet-proof" principle.
+
+
+Conclusion
+==========
+
+We have seen that for-expressions are useful not only for collections.
+
+Many other types also define `map`,`flatMap`, and `withFilter` operations
+and with them for-expressions.
+
+Examples: `Generator`, `Option`, `Try`.
+
+Many of the types defining `flatMap` are monads.
+
+(If they also define `withFilter`, they are called "monads with zero").
+
+The three monad laws give useful guidance in the design of library APIs.
+
+
diff --git a/lectures/progfun2-1-recap1.md b/lectures/progfun2-1-recap1.md
new file mode 100644
index 0000000000000000000000000000000000000000..2bd77ec0210d2e09e5457d6ca7904e6edcdc210a
--- /dev/null
+++ b/lectures/progfun2-1-recap1.md
@@ -0,0 +1,241 @@
+% Recap: Functions and Pattern Matching
+% Martin Odersky
+%
+Recap: Case Classes
+===================
+
+Case classes are Scala's preferred way to define complex data.
+
+\example: Representing JSON (Java Script Object Notation)
+
+\begin{lstlisting}
+  { "firstName" : "John",
+    "lastName" : "Smith",
+    "address": {
+       "streetAddress": "21 2nd Street",
+       "state": "NY",
+       "postalCode": 10021
+    },
+    "phoneNumbers": [
+      { "type": "home", "number": "212 555-1234" },
+      { "type": "fax", "number": "646 555-4567" }
+    ]
+  }
+\end{lstlisting}
+
+Representation of JSON in Scala
+===============================
+
+      abstract class JSON
+      case class JSeq (elems: List[JSON])           extends JSON
+      case class JObj (bindings: Map[String, JSON]) extends JSON
+      case class JNum (num: Double)                 extends JSON
+      case class JStr (str: String)                 extends JSON
+      case class JBool(b: Boolean)                  extends JSON
+      case object JNull                             extends JSON
+
+Example
+=======
+
+      val data = JObj(Map(
+        "firstName" -> JStr("John"),
+        "lastName" -> JStr("Smith"),
+        "address" -> JObj(Map(
+          "streetAddress" -> JStr("21 2nd Street"),
+          "state" -> JStr("NY"),
+          "postalCode" -> JNum(10021)
+        )),
+        "phoneNumbers" -> JSeq(List(
+          JObj(Map(
+            "type" -> JStr("home"), "number" -> JStr("212 555-1234")
+          )),
+          JObj(Map(
+            "type" -> JStr("fax"), "number" -> JStr("646 555-4567")
+          )) )) ))
+
+Pattern Matching
+================
+
+Here's a method that returns the string representation JSON data:
+
+      def show(json: JSON): String = json match {
+        case JSeq(elems) =>
+          "[" + (elems map show mkString ", ") + "]"
+        case JObj(bindings) =>
+          val assocs = bindings map {
+            case (key, value) => "\"" + key + "\": " + show(value)
+          }
+          "{" + (assocs mkString ", ") + "}"
+        case JNum(num) => num.toString
+        case JStr(str) => '\"' + str + '\"'
+        case JBool(b)  => b.toString
+        case JNull     => "null"
+      }
+
+Case Blocks
+===========
+
+\question: What's the type of:
+
+      { case (key, value) => key + ": " + value }
+
+->
+Taken by itself, the expression is not typable.
+
+We need to prescribe an expected type.
+
+The type expected by `map` on the last slide is
+
+      JBinding => String,
+
+the type of functions from pairs of strings and `JSON` data to `String`.
+where `JBinding` is
+
+     type JBinding = (String, JSON)
+
+Functions Are Objects
+=====================
+
+In Scala, every concrete type is the type of some class or trait.
+
+The function type is no exception. A type like
+
+     JBinding => String
+
+is just a shorthand for
+
+     scala.Function1[JBinding, String]
+
+where `scala.Function1` is a trait and `JBinding` and `String` are its type arguments.
+
+The Function1 Trait
+===================
+
+Here's an outline of trait `Function1`:
+
+      trait Function1[-A, +R] {
+        def apply(x: A): R
+      }
+
+The pattern matching block
+
+      { case (key, value) => key + ": " + value }
+
+expands to the `Function1` instance
+
+      new Function1[JBinding, String] {
+        def apply(x: JBinding) = x match {
+          case (key, value) => key + ": " + show(value)
+        }
+      }
+
+Subclassing Functions
+=====================
+
+One nice aspect of functions being traits is that we can subclass the
+function type.
+
+For instance, maps are functions from keys to values:
+
+      trait Map[Key, Value] extends (Key => Value) ...
+->
+Sequences are functions from `Int` indices to values:
+
+      trait Seq[Elem] extends Int => Elem
+
+That's why we can write
+
+      elems(i)
+
+for sequence (and array) indexing.
+
+Partial Matches
+===============
+
+We have seen that a pattern matching block like
+
+     { case "ping" => "pong" }
+
+can be given type `String => String`.
+
+     val f: String => String = { case "ping" => "pong" }
+
+But the function is not defined on all its domain!
+
+     f("pong")      // gives a MatchError
+
+Is there a way to find out whether the function can be applied to a
+given argument before running it?
+
+Partial Functions
+=================
+
+Indeed there is:
+
+      val f: PartialFunction[String, String] =  { case "ping" => "pong" }
+      f.isDefinedAt("ping")         // true
+      f.isDefinedAt("pong")         // false
+
+The partial function trait is defined as follows:
+
+      trait PartialFunction[-A, +R] extends Function1[-A, +R] {
+        def apply(x: A): R
+        def isDefinedAt(x: A): Boolean
+      }
+
+Partial Function Objects
+========================
+
+If the expected type is a `PartialFunction`, the Scala compiler will expand
+
+      { case "ping" => "pong" }
+
+as follows:
+
+      new PartialFunction[String, String] {
+        def apply(x: String) = x match {
+          case "ping" => "pong"
+        }
+        def isDefinedAt(x: String) = x match {
+          case "ping" => true
+          case _ => false
+        }
+      }
+
+
+Exercise
+========
+
+Given the function
+
+      val f: PartialFunction[List[Int], String] = {
+        case Nil => "one"
+        case x :: y :: rest => "two"
+      }
+
+What do you expect is the result of
+
+`    f.isDefinedAt(List(1, 2, 3))` \tab ?
+
+      O   true
+      O   false
+
+\quiz
+
+Exercise(2)
+===========
+
+How about the following variation:
+
+      val g: PartialFunction[List[Int], String] = {
+        case Nil => "one"
+        case x :: rest =>
+          rest match {
+            case Nil => "two"
+          }
+      }
+
+`   g.isDefinedAt(List(1, 2, 3))` \tab gives:
+
+      O   true
+      O   false
diff --git a/lectures/progfun2-1-recap2.md b/lectures/progfun2-1-recap2.md
new file mode 100644
index 0000000000000000000000000000000000000000..5e1fc5b3c2b56bf8f8ba66c81138c68d623860ed
--- /dev/null
+++ b/lectures/progfun2-1-recap2.md
@@ -0,0 +1,181 @@
+% Recap: Collections
+% Martin Odersky
+%
+Recap: Collections
+==================
+
+Scala has a rich hierarchy of collection classes.
+
+
+
+Recap: Collection Methods
+=========================
+
+All collection types share a common set of general methods.
+
+Core methods:
+
+      map
+      flatMap
+      filter
+
+and also
+
+      foldLeft
+      foldRight
+
+Idealized Implementation of `map` on Lists
+==========================================
+
+      abstract class List[+T] {
+
+        def map[U](f: T => U): List[U] = this match {
+          case x :: xs => f(x) :: xs.map(f)
+          case Nil => Nil
+        }
+      }
+
+Idealized Implementation of `flatMap` on Lists
+==============================================
+
+      abstract class List[+T] {
+
+        def flatMap[U](f: T => List[U]): List[U] = this match {
+          case x :: xs => f(x) ++ xs.flatMap(f)
+          case Nil => Nil
+        }
+      }
+
+Idealized Implementation of `filter` on Lists
+=============================================
+
+      abstract class List[+T] {
+
+        def filter(p: T => Boolean): List[T] = this match {
+          case x :: xs =>
+           if p(x) then x :: xs.filter(p) else xs.filter(p)
+          case Nil => Nil
+        }
+      }
+->
+In practice, the implementation and type of these methods are different in order to
+
+ - make them apply to arbitrary collections, not just lists,
+ - make them tail-recursive on lists.
+
+
+For-Expressions
+===============
+
+Simplify combinations of core methods `map`, `flatMap`, `filter`.
+
+Instead of:
+
+    (1 until n) flatMap (i =>
+       (1 until i) filter (j => isPrime(i + j)) map
+         (j => (i, j)))
+
+one can write:
+
+       for {
+         i <- 1 until n
+         j <- 1 until i
+         if isPrime(i + j)
+       } yield (i, j)
+
+
+Translation of For (1)
+======================
+
+The Scala compiler translates for-expressions in terms of `map`, `flatMap`
+and a lazy variant of `filter`.
+
+Here is the translation scheme used by the compiler
+
+$1.$ A simple `for`-expression
+
+      for (x <- e1) yield e2
+
+is translated to
+
+      e1.map(x => e2)
+
+Translation of For (2)
+======================
+
+$2.$ A `for`-expression
+
+      for (x <- e1 if f; s) yield e2
+
+where `f` is a filter and `s` is a (potentially empty) sequence of generators and filters, is translated to
+
+      for (x <- e1.withFilter(x => f); s) yield e2
+
+(and the translation continues with the new expression)
+
+You can think of `withFilter` as a variant of `filter` that does not
+produce an intermediate list, but instead filters the following `map` or `flatMap` function application.
+
+Translation of For (3)
+======================
+
+$3.$ A for-expression
+
+      for (x <- e1; y <- e2; s) yield e3
+
+where `s` is a (potentially empty) sequence of generators and filters, is translated into
+
+      e1.flatMap(x => for (y <- e2; s) yield e3)
+
+(and the translation continues with the new expression)
+
+For-expressions and Pattern Matching
+====================================
+
+The left-hand side of a generator may also be a pattern.
+
+\example
+
+      val data: List[JSON] = ...
+      for {
+        JObj(bindings) <- data
+        JSeq(phones) = bindings("phoneNumbers")
+        JObj(phone) <- phones
+        JStr(digits) = phone("number")
+        if digits startsWith "212"
+      } yield (bindings("firstName"), bindings("lastName"))
+
+Translation of Pattern Matching in For
+======================================
+
+If `pat` is a pattern with a single variable `x`, we translate
+
+      pat <- expr
+
+to:
+
+      x <- expr withFilter {
+             case pat => true
+             case _ => false
+           } map {
+             case pat => x
+           }
+
+Exercise
+========
+
+      for {
+        x <- 2 to N
+        y <- 2 to x
+        if (x % y == 0)
+      } yield (x, y)
+
+The expression above expands to which of the following two expressions?
+
+      O   (2 to N) flatMap (x =>
+            (2 to x) withFilter (y =>
+              x % y == 0) map (y => (x, y)))
+
+      O   (2 to N) map (x =>
+            (2 to x) flatMap (y =>
+              if (x % y) then == 0  (x, y)
diff --git a/lectures/progfun2-2-1.md b/lectures/progfun2-2-1.md
new file mode 100644
index 0000000000000000000000000000000000000000..86559ce58f8944480d67331f6ee77565524b5a68
--- /dev/null
+++ b/lectures/progfun2-2-1.md
@@ -0,0 +1,241 @@
+% Structural Induction on Trees
+%
+%
+Structural Induction on Trees
+=============================
+
+Structural induction is not limited to lists; it applies to any tree structure.
+
+The general induction principle is the following:
+
+To prove a property $\btt P(t)$ for all trees $\btt t$ of a certain type,
+
+ -  show that $\btt P(l)$ holds for all leaves $\btt l$ of a tree,
+ -  for each type of internal node $\btt t$ with subtrees $\btt s_1, ..., s_n$, show that
+\begin{quote}
+        $\btt P(s_1) \wedge ... \wedge P(s_n)$ implies $P(t)$.
+\end{quote}
+
+Example: IntSets
+================
+
+Recall our definition of \verb@IntSet@ with the operations \verb@contains@ and \verb@incl@:
+
+      abstract class IntSet {
+         def incl(x: Int): IntSet
+         def contains(x: Int): Boolean
+      }
+
+      object Empty extends IntSet {
+        def contains(x: Int): Boolean = false
+        def incl(x: Int): IntSet = NonEmpty(x, Empty, Empty)
+      }
+
+Example: IntSets (2)
+====================
+
+      case class NonEmpty(elem: Int, left: IntSet, right: IntSet) extends IntSet {
+
+        def contains(x: Int): Boolean =
+          if x < elem then left contains x
+          else if x > elem then right contains x
+          else true
+
+        def incl(x: Int): IntSet =
+          if x < elem then NonEmpty(elem, left incl x, right)
+          else if x > elem then NonEmpty(elem, left, right incl x)
+          else this
+      }
+
+
+The Laws of IntSet
+==================
+
+What does it mean to prove the correctness of this implementation?
+
+One way to define and show the correctness of an implementation
+consists of proving the laws that it respects.
+
+In the case of \verb@IntSet@, we have the following three laws:
+
+For any set \verb@s@, and elements \verb@x@ and \verb@y@:
+
+      Empty contains x          =  false
+      (s incl x) contains x     =  true
+      (s incl x) contains y     =  s contains y         if x != y
+
+
+(In fact, we can show that these laws completely characterize the desired data type).
+
+Proving the Laws of IntSet (1)
+==============================
+
+How can we prove these laws?
+
+\red{Proposition 1}: `Empty contains x =  false`.
+
+\red{Proof:} According to the definition of `contains` in `Empty`.
+
+Proving the Laws of IntSet (2)
+==============================
+
+\red{Proposition 2:} `(s incl x) contains x = true`
+
+Proof by structural induction on `s`.
+
+\BaseCase{\tt Empty}
+
+
+       (Empty incl x) contains x
+->
+       =   NonEmpty(x, Empty, Empty) contains x   // by definition of Empty.incl
+->
+       =   true                                   // by definition of NonEmpty.contains
+
+Proving the Laws of IntSet (3)
+==============================
+
+\InductionStep{\tt NonEmpty(x, l, r)}
+
+
+       (NonEmpty(x, l, r) incl x) contains x
+->
+       =   NonEmpty(x, l, r) contains x          //  by definition of NonEmpty.incl
+->
+       =   true                                  //  by definition of NonEmpty.contains
+
+Proving the Laws of IntSet (4)
+==============================
+
+\InductionStep{{\tt NonEmpty(y, l, r)} where {\tt y < x}}
+
+       (NonEmpty(y, l, r) incl x) contains x
+->
+       =   NonEmpty(y, l, r incl x) contains x   // by definition of NonEmpty.incl
+->
+       =   (r incl x) contains x                 // by definition of NonEmpty.contains
+->
+       =   true                                  // by the induction hypothesis
+->
+\medskip
+
+\InductionStep{{\tt NonEmpty(y, l, r)} where {\tt y > x}} is analogous
+
+Proving the Laws of IntSet (5)
+==============================
+
+\red{Proposition 3}: If `x != y` then
+
+      (xs incl y) contains x  =  xs contains x.
+
+Proof by structural induction on `s`. Assume that `y < x` (the dual case `x < y` is analogous).
+
+\BaseCase{\tt Empty}
+
+      (Empty incl y) contains x                  // to show: =  Empty contains x
+->
+      =   NonEmpty(y, Empty, Empty) contains x   // by definition of Empty.incl
+->
+      =   Empty contains x                       // by definition of NonEmpty.contains
+
+Proving the Laws of IntSet (6)
+==============================
+
+For the inductive step, we need to consider a tree `NonEmpty(z, l, r)`. We distinguish five cases:
+
+ 1. `z = x`
+ 2. `z = y`
+ 3. `z < y < x`
+ 4. `y < z < x`
+ 5. `y < x < z`
+
+First Two Cases: `z = x, z = y`
+===============================
+
+\InductionStep{\tt NonEmpty(x, l, r)}
+
+      (NonEmpty(x, l, r) incl y) contains x  // to show: =  NonEmpty(x, l, r) contains x
+->
+      =   NonEmpty(x, l incl y, r) contains x    // by definition of NonEmpty.incl
+->
+      =   true                                   // by definition of NonEmpty.contains
+->
+      =   NonEmpty(x, l, r) contains x           // by definition of NonEmpty.contains
+->
+\InductionStep{\tt NonEmpty(y, l, r)}
+
+      (NonEmpty(y, l, r) incl y) contains x  // to show: =  NonEmpty(y, l, r) contains x
+->
+      =    NonEmpty(y, l, r) contains x          // by definition of NonEmpty.incl
+
+Case `z < y`
+============
+
+\InductionStep{{\tt NonEmpty(z, l, r)} where {\tt z < y < x}}
+
+      (NonEmpty(z, l, r) incl y) contains x  // to show: =  NonEmpty(z, l, r) contains x
+->
+      =   NonEmpty(z, l, r incl y) contains x    // by definition of NonEmpty.incl
+->
+      =   (r incl y) contains x                  // by definition of NonEmpty.contains
+->
+      =   r contains x                           // by the induction hypothesis
+->
+      =   NonEmpty(z, l, r) contains x           // by definition of NonEmpty.contains
+
+Case `y < z < x`
+================
+
+\InductionStep{{\tt NonEmpty(z, l, r)} where {\tt y < z < x}}
+
+      (NonEmpty(z, l, r) incl y) contains x  // to show: =  NonEmpty(z, l, r) contains x
+->
+      =   NonEmpty(z, l incl y, r) contains x    // by definition of NonEmpty.incl
+->
+      =   r contains x                           // by definition of NonEmpty.contains
+->
+      =   NonEmpty(z, l, r) contains x           // by definition of NonEmpty.contains
+
+Case `x < z`
+============
+
+\InductionStep{{\tt NonEmpty(z, l, r)} where {\tt y < x < z}}
+
+      (NonEmpty(z, l, r) incl y) contains x  // to show: =  NonEmpty(z, l, r) contains x
+->
+      =   NonEmpty(z, l incl y, r) contains x    // by definition of NonEmpty.incl
+->
+      =   (l incl y) contains x                  // by definition of NonEmpty.contains
+->
+      =   l contains x                           // by the induction hypothesis
+->
+      =   NonEmpty(z, l, r) contains x           // by definition of NonEmpty.contains
+
+These are all the cases, so the proposition is established.
+
+Exercise (Hard)
+===============
+
+Suppose we add a function `union` to `IntSet`:
+
+      abstract class IntSet { ...
+        def union(other: IntSet): IntSet
+      }
+      object Empty extends IntSet { ...
+        def union(other: IntSet) = other
+      }
+      class NonEmpty(x: Int, l: IntSet, r: IntSet) extends IntSet { ...
+        def union(other: IntSet): IntSet = (l union (r union (other))) incl x
+      }
+
+Exercise (Hard)
+===============
+
+The correctness of `union` can be translated into the following law:
+
+\red{Proposition 4}:
+
+      (xs union ys) contains x  =  xs contains x || ys contains x
+
+Show proposition 4 by using structural induction on `xs`.
+
diff --git a/lectures/progfun2-2-2.md b/lectures/progfun2-2-2.md
new file mode 100644
index 0000000000000000000000000000000000000000..ac371c1d539fa6080110708c51aff82130826256
--- /dev/null
+++ b/lectures/progfun2-2-2.md
@@ -0,0 +1,203 @@
+% Streams
+%
+%
+
+Collections and Combinatorial Search
+====================================
+
+We've seen a number of immutable collections that provide powerful operations, in particular for
+combinatorial search.
+
+For instance, to find the second prime number between 1000 and 10000:
+
+      ((1000 to 10000) filter isPrime)(1)
+
+This is _much_ shorter than the recursive alternative:
+
+      def secondPrime(from: Int, to: Int) = nthPrime(from, to, 2)
+      def nthPrime(from: Int, to: Int, n: Int): Int =
+        if from >= to then throw new Error("no prime")
+        else if isPrime(from) then
+          if n == 1 then from else nthPrime(from + 1, to, n - 1)
+        else nthPrime(from + 1, to, n)
+
+Performance Problem
+===================
+
+But from a standpoint of performance,
+
+      ((1000 to 10000) filter isPrime)(1)
+
+is pretty bad; it constructs _all_ prime numbers between `1000` and
+`10000` in a list, but only ever looks at the first two elements of that list.
+
+Reducing the upper bound would speed things up, but risks that we miss the
+second prime number all together.
+
+Delayed Evaluation
+==================
+
+However, we can make the short-code efficient by using a trick:
+
+>  Avoid computing the tail of a sequence until it is needed for the evaluation result (which might be never)
+
+This idea is implemented in a new class, the `Stream`.
+
+Streams are similar to lists, but their tail is evaluated only _on demand_.
+
+Defining Streams
+================
+
+Streams are defined from a constant `Stream.empty` and a constructor `Stream.cons`.
+
+For instance,
+
+     val xs = Stream.cons(1, Stream.cons(2, Stream.empty))
+
+They can also be defined like the other collections by using the
+object `Stream` as a factory.
+
+     Stream(1, 2, 3)
+
+The `toStream` method on a collection will turn the collection into a stream:
+
+\begin{worksheet}
+    \verb` (1 to 1000).toStream  ` \wsf res0: Stream[Int] = Stream(1, ?)
+\end{worksheet}
+
+
+Stream Ranges
+=============
+
+Let's try to write a function that returns `(lo until hi).toStream` directly:
+
+      def streamRange(lo: Int, hi: Int): Stream[Int] =
+        if lo >= hi then Stream.empty
+        else Stream.cons(lo, streamRange(lo + 1, hi))
+
+Compare to the same function that produces a list:
+
+      def listRange(lo: Int, hi: Int): List[Int] =
+        if lo >= hi then Nil
+        else lo :: listRange(lo + 1, hi)
+
+Comparing the Two Range Functions
+=================================
+
+The functions have almost identical structure yet they evaluate quite differently.
+
+- `listRange(start, end)` will produce a list with `end - start` elements and return it.
+
+- `streamRange(start, end)` returns a single object of type `Stream` with `start` as head element.
+
+- The other elements are only computed when they are needed, where
+  "needed" means that someone calls `tail` on the stream.
+
+Methods on Streams
+==================
+
+`Stream` supports almost all methods of `List`.
+
+For instance, to find the second prime number between 1000 and 10000:
+
+      ((1000 to 10000).toStream filter isPrime)(1)
+
+Stream Cons Operator
+====================
+
+The one major exception is `::`.
+
+`x :: xs` always produces a list, never a stream.
+
+There is however an alternative operator `#::` which produces a stream.
+
+       x #:: xs  ==   Stream.cons(x, xs)
+
+`#::` can be used in expressions as well as patterns.
+
+Implementation of Streams
+=========================
+
+The implementation of streams is quite close to the one of lists.
+
+Here's the trait `Stream`:
+
+      trait Stream[+A] extends Seq[A] {
+        def isEmpty: Boolean
+        def head: A
+        def tail: Stream[A]
+        ...
+      }
+
+As for lists, all other methods can be defined in terms of these three.
+
+Implementation of Streams(2)
+============================
+
+Concrete implementations of streams are defined in the `Stream` companion object.
+Here's a first draft:
+
+      object Stream {
+        def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
+          def isEmpty = false
+          def head = hd
+          def tail = tl
+          override def toString = "Stream(" + hd + ", ?)"
+        }
+        val empty = new Stream[Nothing] {
+          def isEmpty = true
+          def head = throw new NoSuchElementException("empty.head")
+          def tail = throw new NoSuchElementException("empty.tail")
+          override def toString = "Stream()"
+        }
+      }
+
+Difference to List
+==================
+
+The only important difference between the implementations of `List` and `Stream`
+concern `tl`, the second parameter of `Stream.cons`.
+
+For streams, this is a by-name parameter.
+
+That's why the second argument to `Stream.cons` is not evaluated at the point of call.
+
+Instead, it will be evaluated each time someone calls `tail` on a `Stream` object.
+
+Other Stream Methods
+====================
+
+The other stream methods are implemented analogously to their list counterparts.
+
+For instance, here's `filter`:
+
+      class Stream[+T] {
+        ...
+        def filter(p: T => Boolean): Stream[T] =
+          if isEmpty then this
+          else if p(head) then cons(head, tail.filter(p))
+          else tail.filter(p)
+      }
+
+Exercise
+========
+
+Consider this modification of `streamRange`.
+
+      def streamRange(lo: Int, hi: Int): Stream[Int] = {
+        print(lo+" ")
+        if lo >= hi then Stream.empty
+        else Stream.cons(lo, streamRange(lo + 1, hi))
+      }
+
+When you write `streamRange(1, 10).take(3).toList` \newline
+what gets printed?
+
+     O          Nothing
+     O          1
+     O          1 2 3
+     O          1 2 3 4
+     O          1 2 3 4 5 6 7 8 9
+->
+
+\quiz
\ No newline at end of file
diff --git a/lectures/progfun2-2-3.md b/lectures/progfun2-2-3.md
new file mode 100644
index 0000000000000000000000000000000000000000..bb90d121eaab5ef5ba1172be9775b97dfeeddcc4
--- /dev/null
+++ b/lectures/progfun2-2-3.md
@@ -0,0 +1,176 @@
+% Lazy Evaluation
+%
+%
+
+Lazy Evaluation
+===============
+
+The proposed implementation suffers from a serious potential performance
+problem: If `tail` is called several times, the corresponding stream
+will be recomputed each time.
+
+This problem can be avoided by storing the result of the first
+evaluation of `tail` and re-using the stored result instead of recomputing `tail`.
+
+This optimization is sound, since in a purely functional language an
+expression produces the same result each time it is evaluated.
+
+We call this scheme \red{lazy evaluation} (as opposed to \red{by-name evaluation} in the case where everything is recomputed, and \red{strict evaluation} for normal parameters and `val` definitions.)
+
+Lazy Evaluation in Scala
+========================
+
+Haskell is a functional programming language that uses lazy evaluation by default.
+
+Scala uses strict evaluation by default, but allows lazy evaluation of value definitions
+with the `lazy val` form:
+
+      lazy val x = expr
+
+
+Exercise:
+=========
+
+Consider the following program:
+
+      def expr = {
+        val x = { print("x"); 1 }
+        lazy val y = { print("y"); 2 }
+        def z = { print("z"); 3 }
+        z + y + x + z + y + x
+     }
+     expr
+
+If you run this program, what gets printed as a side effect of evaluating `expr`?
+
+      O      zyxzyx                    O      xzyz
+      O      xyzz                      O      zyzz
+      O      something else
+
+
+
+
+Lazy Vals and Streams
+=====================
+
+Using a lazy value for `tail`, `Stream.cons` can be implemented more efficiently:
+
+      def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
+        def head = hd
+        lazy val tail = tl
+        ...
+      }
+
+Seeing it in Action
+===================
+
+To convince ourselves that the implementation of streams
+really does avoid unnecessary computation, let's observe the
+execution trace of the expression:
+
+          (streamRange(1000, 10000) filter isPrime) apply 1
+->
+     -->  (if 1000 >= 10000 then empty             // by expanding streamRange
+           else cons(1000, streamRange(1000 + 1, 10000))
+          .filter(isPrime).apply(1)
+->
+     -->  cons(1000, streamRange(1000 + 1, 10000))  // by evaluating if
+          .filter(isPrime).apply(1)
+
+Evaluation Trace (2)
+====================
+
+Let's abbreviate `cons(1000, streamRange(1000 + 1, 10000))` to `C1`.
+
+          C1.filter(isPrime).apply(1)
+->
+     -->  (if C1.isEmpty then C1                   // by expanding filter
+           else if isPrime(C1.head) then cons(C1.head, C1.tail.filter(isPrime))
+           else C1.tail.filter(isPrime))
+          .apply(1)
+->
+     -->  (if isPrime(C1.head) then cons(C1.head, C1.tail.filter(isPrime))
+           else C1.tail.filter(isPrime))        // by eval. if
+          .apply(1)
+->
+     -->  (if isPrime(1000) then cons(C1.head, C1.tail.filter(isPrime))
+           else C1.tail.filter(isPrime))        // by eval. head
+          .apply(1)
+
+Evaluation Trace (3)
+====================
+
+     -->> (if false then cons(C1.head, C1.tail.filter(isPrime))  // by eval. isPrime
+           else C1.tail.filter(isPrime))
+          .apply(1)
+->
+     -->  C1.tail.filter(isPrime).apply(1)                    // by eval. if
+->
+     -->> streamRange(1001, 10000)                            // by eval. tail
+          .filter(isPrime).apply(1)
+
+The evaluation sequence continues like this until:
+->
+     -->> streamRange(1009, 10000)
+          .filter(isPrime).apply(1)
+
+     -->  cons(1009, streamRange(1009 + 1, 10000))            // by eval. streamRange
+          .filter(isPrime).apply(1)
+
+Evaluation Trace (4)
+====================
+
+Let's abbreviate `cons(1009, streamRange(1009 + 1, 10000))` to `C2`.
+
+          C2.filter(isPrime).apply(1)
+->
+     -->  cons(1009, C2.tail.filter(isPrime)).apply(1)
+->
+     -->  if 1 == 0 then cons(1009, C2.tail.filter(isPrime)).head // by eval. apply
+          else cons(1009, C2.tail.filter(isPrime)).tail.apply(0)
+
+Assuming `apply` is defined like this in `Stream[T]`:
+
+      def apply(n: Int): T =
+        if n == 0 then head
+        else tail.apply(n-1)
+
+Evaluation Trace (4)
+====================
+
+Let's abbreviate `cons(1009, streamRange(1009 + 1, 10000))` to `C2`.
+
+          C2.filter(isPrime).apply(1)
+
+     -->> cons(1009, C2.tail.filter(isPrime)).apply(1)         // by eval. filter
+
+     -->  if 1 == 0 then cons(1009, C2.tail.filter(isPrime)).head // by eval. apply
+          else cons(1009, C2.tail.filter(isPrime)).tail.apply(0)
+
+     -->  cons(1009, C2.tail.filter(isPrime)).tail.apply(0)    // by eval. if
+->
+     -->  C2.tail.filter(isPrime).apply(0)                     // by eval. tail
+->
+     -->  streamRange(1010, 10000).filter(isPrime).apply(0)    // by eval. tail
+
+Evaluation Trace (5)
+====================
+
+The process continues until
+
+          ...
+     -->  streamRange(1013, 10000).filter(isPrime).apply(0)
+->
+     -->  cons(1013, streamRange(1013 + 1, 10000))           // by eval. streamRange
+          .filter(isPrime).apply(0)
+
+Let `C3` be a shorthand for `cons(1013, streamRange(1013 + 1, 10000)`.
+
+     ==   C3.filter(isPrime).apply(0)
+->
+     -->> cons(1013, C3.tail.filter(isPrime)).apply(0)      // by eval. filter
+->
+     -->  1013                                              // by eval. apply
+
+Only the part of the stream necessary to compute the result has been constructed.
+
diff --git a/lectures/progfun2-2-4.md b/lectures/progfun2-2-4.md
new file mode 100644
index 0000000000000000000000000000000000000000..38b93c704a8484d12b6ce27c3bcb0c94c8d030e8
--- /dev/null
+++ b/lectures/progfun2-2-4.md
@@ -0,0 +1,92 @@
+% Computing with Infinite Sequences
+%
+%
+
+Infinite Streams
+================
+
+You saw that all elements of a stream except the first one are computed
+only when they are needed to produce a result.
+
+This opens up the possibility to define infinite streams!
+
+For instance, here is the stream of all integers starting from a given number:
+
+      def from(n: Int): Stream[Int] = n #:: from(n+1)
+
+The stream of all natural numbers:
+->
+      val nats = from(0)
+
+The stream of all multiples of 4:
+->
+      nats map (_ * 4)
+
+The Sieve of Eratosthenes
+=========================
+
+The Sieve of Eratosthenes is an ancient technique to calculate prime numbers.
+
+The idea is as follows:
+
+ - Start with all integers from 2, the first prime number.
+ - Eliminate all multiples of 2.
+ - The first element of the resulting list is 3, a prime number.
+ - Eliminate all multiples of 3.
+ - Iterate forever. At each step, the first number in the list is a prime number
+   and we eliminate all its multiples.
+
+The Sieve of Eratosthenes in Code
+=================================
+
+Here's a function that implements this principle:
+
+      def sieve(s: Stream[Int]): Stream[Int] =
+        s.head #:: sieve(s.tail filter (_ % s.head != 0))
+
+      val primes = sieve(from(2))
+
+To see the list of the first `N` prime numbers, you can write
+
+      (primes take N).toList
+
+Back to Square Roots
+====================
+
+Our previous algorithm for square roots always used a `isGoodEnough` test to tell when to terminate the iteration.
+
+With streams we can now express the concept of a converging sequence without
+having to worry about when to terminate it:
+
+      def sqrtStream(x: Double): Stream[Double] = {
+        def improve(guess: Double) = (guess + x / guess) / 2
+        lazy val guesses: Stream[Double] = 1 #:: (guesses map improve)
+        guesses
+      }
+
+Termination
+===========
+
+We can add `isGoodEnough` later.
+
+      def isGoodEnough(guess: Double, x: Double) =
+        math.abs((guess * guess - x) / x) < 0.0001
+
+      sqrtStream(4) filter (isGoodEnough(_, 4))
+
+Exercise:
+=========
+
+Consider two ways to express the infinite stream of multiples of a given number `N`:
+
+      val xs = from(1) map (_ * N)
+
+      val ys = from(1) filter (_ % N == 0)
+
+Which of the two streams generates its results faster?
+
+      O        from(1) map (_ * N)
+      O        from(1) filter (_ % N == 0)
+
+->
+\quiz
\ No newline at end of file
diff --git a/lectures/progfun2-2-5.md b/lectures/progfun2-2-5.md
new file mode 100644
index 0000000000000000000000000000000000000000..ecc009ce47ff9c51d1ec2ab7f9a84bc8c1cda736
--- /dev/null
+++ b/lectures/progfun2-2-5.md
@@ -0,0 +1,41 @@
+% Case Study
+%
+%
+The Water Pouring Problem
+=========================
+
+
+
+States and Moves
+================
+
+Glass: `Int`
+
+State: `Vector[Int]` (one entry per glass)
+
+Moves:
+
+      Empty(glass)
+      Fill(glass)
+      Pour(from, to)
+
+Variants
+========
+
+In a program of the complexity of the pouring program, there are many
+choices to be made.
+
+Choice of representations.
+
+ - Specific classes for moves and paths, or some encoding?
+ - Object-oriented methods, or naked data structures with functions?
+
+The present elaboration is just one solution, and not necessarily the
+shortest one.
+
+Guiding Principles for Good Design
+==================================
+
+ - Name everything you can.
+ - Put operations into natural scopes.
+ - Keep degrees of freedom for future refinements.
diff --git a/lectures/progfun2-3-1.md b/lectures/progfun2-3-1.md
new file mode 100644
index 0000000000000000000000000000000000000000..64edea43afdab6e0c31dd73e6deea84f7306bc4a
--- /dev/null
+++ b/lectures/progfun2-3-1.md
@@ -0,0 +1,208 @@
+% Functions and State
+% Martin Odersky
+%
+
+Functions and State
+===================
+
+Until now, our programs have been side-effect free.
+
+Therefore, the concept of \red{time} wasn't important.
+
+For all programs that terminate, any sequence of actions would have given the same results.
+
+This was also reflected in the substitution model of computation.
+
+Reminder: Substitution Model
+============================
+
+Programs can be evaluated by \red{rewriting}.
+
+The most important rewrite rule covers function applications:
+
+$$
+\begin{array}{ll}
+  &\btt def\ f (x_1, ..., x_n) = B ;\ ...\ f (v_1, ..., v_n)    \\
+ \rightarrow & \\
+  &\btt def\ f (x_1, ..., x_n) = B ;\ ...\ [v_1/x_1, ..., v_n/x_n]\,B
+\end{array}
+$$
+
+Rewriting Example:
+==================
+
+Say you have the following two functions `iterate` and `square`:
+
+    def iterate(n: Int, f: Int => Int, x: Int) =
+      if n == 0 then x else iterate(n-1, f, f(x))
+    def square(x: Int) = x * x
+
+Then the call `iterate(1, square, 3)` gets rewritten as follows:
+->
+   $\rightarrow$ `  if 1 == 0 then 3 else iterate(1-1, square, square(3))`
+->
+   $\rightarrow$ `  iterate(0, square, square(3))`
+->
+   $\rightarrow$ `  iterate(0, square, 3 * 3)`
+->
+   $\rightarrow$ `  iterate(0, square, 9)`
+->
+   $\rightarrow$ `  if 0 == 0 then 9 else iterate(0-1, square, square(9)) ` $\rightarrow$ ` 9`
+
+Observation:
+============
+
+Rewriting can be done anywhere in a term, and all rewritings which
+terminate lead to the same solution.
+
+This is an important result of the $\lambda$-calculus, the theory
+behind functional programming.
+
+Example:
+
+         if 1 == 0 then 3 else iterate(1 - 1, square, square(3))
+->
+  \mbox{}
+
+     iterate(0, square, square(3))
+
+
+Observation:
+============
+
+Rewriting can be done anywhere in a term, and all rewritings which
+terminate lead to the same solution.
+
+This is an important result of the $\lambda$-calculus, the theory
+behind functional programming.
+
+Example:
+
+         if 1 == 0 then 3 else iterate(1 - 1, square, square(3))
+
+  \mbox{}
+
+     iterate(0, square, square(3))     if 1 == 0 then 3
+                                       else iterate(1 - 1, square, 3 * 3)
+
+
+
+Stateful Objects
+================
+
+One normally describes the world as a set of objects, some of which
+have state that \red{changes} over the course of time.
+
+An object \red{has a state} if its behavior is influenced by its
+history.
+
+\example:
+a bank account has a state, because the answer to the question
+\begin{quote}
+  ``can I withdraw 100 CHF ?''
+\end{quote}
+may vary over the course of the lifetime of the account.
+
+Implementation of State
+=======================
+
+Every form of mutable state is constructed from variables.
+
+A variable definition is written like a value definition, but with the
+keyword `var` in place of `val`:
+
+      var x: String = "abc"
+      var count = 111
+
+Just like a value definition, a variable definition associates a value
+with a name.
+
+However, in the case of variable definitions, this association can be
+changed later through an \red{assignment}, like in Java:
+
+      x = "hi"
+      count = count + 1
+
+State in Objects
+================
+
+In practice, objects with state are usually represented by objects that
+have some variable members. \example: Here is a class modeling a bank account.
+
+      class BankAccount {
+        private var balance = 0
+        def deposit(amount: Int): Unit = {
+          if amount > 0 then balance = balance + amount
+        }
+        def withdraw(amount: Int): Int =
+          if 0 < amount && amount <= balance then {
+            balance = balance - amount
+            balance
+          } else throw new Error("insufficient funds")
+      }
+
+State in Objects (2)
+====================
+
+The class `BankAccount` defines a variable `balance` that contains the
+current balance of the account.
+
+The methods `deposit` and `withdraw` change the value of the `balance`
+through assignments.
+
+Note that `balance` is `private` in the `BankAccount`
+class, it therefore cannot be accessed from outside the class.
+
+To create bank accounts, we use the usual notation for object creation:
+
+      val account = new BankAccount
+
+Working with Mutable Objects
+============================
+Here is a worksheet that manipulates bank accounts.
+
+      val account = new BankAccount       // account: BankAccount = BankAccount
+      account deposit 50                  //
+      account withdraw 20                 // res1: Int = 30
+      account withdraw 20                 // res2: Int = 10
+      account withdraw 15                 // java.lang.Error: insufficient funds
+
+Applying the same operation to an account twice in a row produces different results. Clearly, accounts are stateful objects.
+
+Statefulness and Variables
+==========================
+
+Remember the definition of streams (lazy sequences) in \blue{week 7, \#progfun]}. Instead of using a lazy val, we could also
+implement non-empty streams using a mutable variable:
+
+      def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
+        def head = hd
+        private var tlOpt: Option[Stream[T]] = None
+        def tail: T = tlOpt match {
+          case Some(x) => x
+          case None => tlOpt = Some(tl); tail
+        }}
+
+\question: Is the result of `cons` a stateful object?
+
+       O        Yes
+       O        No
+
+\quiz
+
+Statefulness and Variables (2)
+==============================
+
+Consider the following class:
+
+      class BankAccountProxy(ba: BankAccount) {
+        def deposit(amount: Int): Unit = ba.deposit(amount)
+        def withdraw(amount: Int): Int = ba.withdraw(amount)
+      }
+
+\question: Are instances of `BankAccountProxy` stateful objects?
+
+       O        Yes
+       O        No
+
+\quiz
diff --git a/lectures/progfun2-3-2.md b/lectures/progfun2-3-2.md
new file mode 100644
index 0000000000000000000000000000000000000000..53ca27b3f16baadbb1989ecc75dab26668867f5b
--- /dev/null
+++ b/lectures/progfun2-3-2.md
@@ -0,0 +1,153 @@
+% Identity and Change
+% Martin Odersky
+%
+
+Identity and Change
+===================
+
+Assignment poses the new problem of deciding whether two expressions
+are "the same"
+
+When one excludes assignments and one writes:
+
+      val x = E; val y = E
+
+where `E` is an arbitrary expression, then it is reasonable to assume that `x` and `y` are the same. That is to say that we could have also written:
+
+      val x = E; val y = x
+
+(This property is usually called \red{referential transparency})
+
+Identity and Change (2)
+=======================
+
+But once we allow the assignment, the two formulations are different. For example:
+
+      val x = new BankAccount
+      val y = new BankAccount
+
+\question: Are `x` and `y` the same?
+
+      O       Yes
+      O       No
+
+\quiz
+
+
+Operational Equivalence
+=======================
+
+To respond to the last question, we must specify what is meant by "the same".
+
+The precise meaning of "being the same" is defined by the property of
+\red{operational equivalence}.
+
+In a somewhat informal way, this property is stated as follows.
+
+Suppose we have two definitions `x` and `y`.
+
+`x` and `y` are operationally equivalent if \red{no possible test} can
+distinguish between them.
+
+Testing for Operational Equivalence
+===================================
+
+To test if `x` and `y` are the same, we must
+
+- Execute the definitions followed by an arbitrary sequence `f` of operations that involves `x` and `y`, observing the possible outcomes.
+
+          val x = new BankAccount
+          val y = new BankAccount
+          f(x, y)
+
+Testing for Operational Equivalence
+===================================
+
+To test if `x` and `y` are the same, we must
+
+- Execute the definitions followed by an arbitrary sequence of operations that involves `x` and `y`, observing the possible outcomes.
+
+          val x = new BankAccount             val x = new BankAccount
+          val y = new BankAccount             val y = new BankAccount
+          f(x, y)                             f(x, x)
+
+- Then, execute the definitions with another sequence `S'` obtained by renaming all occurrences of `y` by `x` in `S`
+->
+- If the results are different, then the expressions `x` and `y` are certainly different.
+->
+- On the other hand, if all possible pairs of sequences `(S, S')` produce the same result, then `x` and `y` are the same.
+
+Counterexample for Operational Equivalence
+==========================================
+
+Based on this definition, let's see if the expressions
+
+      val x = new BankAccount
+      val y = new BankAccount
+
+define values `x` and `y` that are the same.
+
+Let's follow the definitions by a test sequence:
+
+      val x = new BankAccount
+      val y = new BankAccount
+      x deposit 30                    // val res1: Int = 30
+      y withdraw 20                   // java.lang.Error: insufficient funds
+
+Counterexample for Operational Equivalence (2)
+==============================================
+
+Now rename all occurrences of `y` with `x` in this sequence. We obtain:
+
+      val x = new BankAccount
+      val y = new BankAccount
+      x deposit 30                    // val res1: Int = 30
+      x withdraw 20                   // val res2: Int = 10
+
+The final results are different. We conclude that `x` and `y` are not the same.
+
+Establishing Operational Equivalence
+====================================
+
+On the other hand, if we define
+
+      val x = new BankAccount
+      val y = x
+
+then no sequence of operations can distinguish between `x` and `y`, so
+`x` and `y` are the same in this case.
+
+Assignment and Substitution Model
+=================================
+
+The preceding examples show that our model of computation by
+substitution cannot be used.
+
+Indeed, according to this model, one can always replace the name of a value by the expression that defines it. For example, in
+
+      val x = new BankAccount
+      val y = x
+
+the `x` in the definition of `y` could be replaced by `new BankAccount`
+
+Assignment and The Substitution Model
+=====================================
+
+The preceding examples show that our model of computation by
+substitution cannot be used.
+
+Indeed, according to this model, one can always replace the name of a value by the expression that defines it. For example, in
+
+      val x = new BankAccount            val x = new BankAccount
+      val y = x                          val y = new BankAccount
+
+the `x` in the definition of `y` could be replaced by `new BankAccount`
+
+But we have seen that this change leads to a different program!
+
+The substitution model ceases to be valid when we add the assignment.
+
+It is possible to adapt the substitution model by introducing a \red{store},
+but this becomes considerably more complicated.
+\bigskip\bigskip
+
diff --git a/lectures/progfun2-3-3.md b/lectures/progfun2-3-3.md
new file mode 100644
index 0000000000000000000000000000000000000000..8a2d173093c0ec48e66ac29a6061804df1f259fb
--- /dev/null
+++ b/lectures/progfun2-3-3.md
@@ -0,0 +1,109 @@
+% Loops
+% Martin Odersky
+%
+
+Loops
+=====
+
+\red{Proposition:} Variables are enough to model all imperative programs.
+
+But what about control statements like loops?
+
+We can model them using functions.
+
+\example: Here is a Scala program that uses a `while` loop:
+
+      def power (x: Double, exp: Int): Double = {
+        var r = 1.0
+        var i = exp
+        while (i > 0) { r = r * x; i = i - 1 }
+        r
+      }
+
+In Scala, `while` is a keyword.
+
+But how could we define `while` using a function (call it `WHILE`)?
+
+
+Definition of `while`
+====================
+
+The function `WHILE` can be defined as follows:
+
+      def WHILE(condition: => Boolean)(command: => Unit): Unit =
+        if condition then {
+          command
+          WHILE(condition)(command)
+        }
+        else ()
+
+\red{Note:} The condition and the command must be passed by name so that they're reevaluated in each iteration.
+
+\red{Note:} `WHILE` is tail recursive, so it can operate with a constant
+stack size.
+
+Exercise
+========
+
+Write a function implementing a `repeat` loop that is used as follows:
+
+      REPEAT {
+        command
+      } ( condition )
+
+It should execute `command` one or more times, until `condition` is true.
+\quiz
+->
+The `REPEAT` function starts like this:
+
+      def REPEAT(command: => Unit)(condition: => Boolean) =
+
+Exercise (open-ended)
+=====================
+
+Is it also possible to obtain the following syntax?
+
+      REPEAT {
+        command
+      } UNTIL ( condition )
+
+?
+
+
+For-Loops
+=========
+
+The classical `for` loop in Java can _not_ be modeled simply by
+a higher-order function.
+
+The reason is that in a Java program like
+
+      for (int i = 1; i < 3; i = i + 1) { System.out.print(i + " "); }
+
+the arguments of `for` contain the \red{declaration} of the variable
+`i`, which is visible in other arguments and in the body.
+
+However, in Scala there is a kind of `for` loop similar to Java's extended for loop:
+
+      for (i <- 1 until 3) { System.out.print(i + " ") }
+
+This displays `1 2`.
+
+Translation of For-Loops
+========================
+
+For-loops translate similarly to for-expressions, but using the `foreach` combinator instead of `map` and `flatMap`.
+
+`foreach` is defined on collections with elements of type `T` as follows:
+
+      def foreach(f: T => Unit): Unit =
+        // apply `f` to each element of the collection
+
+\example
+
+      for (i <- 1 until 3; j <- "abc") println(i + " " + j)
+
+translates to:
+
+     (1 until 3) foreach (i => "abc" foreach (j => println(i + " " + j)))
+
diff --git a/lectures/progfun2-3-4.md b/lectures/progfun2-3-4.md
new file mode 100644
index 0000000000000000000000000000000000000000..cd27a203a80e519ae732dc49bd9b5c298b0399ce
--- /dev/null
+++ b/lectures/progfun2-3-4.md
@@ -0,0 +1,127 @@
+% Imperative Event Handling: The Observer Pattern
+% Martin Odersky
+%
+The Observer Pattern
+====================
+
+The Observer Pattern is widely used when views need to react to changes in
+a model.
+
+Variants of it are also called
+
+ - publish/subscribe
+ - model/view/controller (MVC).
+
+
+A Publisher Trait
+=================
+
+      trait Publisher {
+
+        private var subscribers: Set[Subscriber] = Set()
+
+        def subscribe(subscriber: Subscriber): Unit =
+          subscribers += subscriber
+
+        def unsubscribe(subscriber: Subscriber): Unit =
+          subscribers -= subscriber
+
+        def publish(): Unit =
+          subscribers.foreach(_.handler(this))
+      }
+
+A Subscriber Trait
+==================
+
+      trait Subscriber {
+        def handler(pub: Publisher)
+      }
+
+
+Observing Bank Accounts
+=======================
+
+Let's make `BankAccount` a `Publisher`:
+
+      class BankAccount extends Publisher {
+        private var balance = 0
+
+        def deposit(amount: Int): Unit =
+          if amount > 0 then {
+            balance = balance + amount
+
+          }
+        def withdraw(amount: Int): Unit =
+          if 0 < amount && amount <= balance then {
+            balance = balance - amount
+
+          } else throw new Error("insufficient funds")
+      }
+
+Observing Bank Accounts
+=======================
+
+Let's make `BankAccount` a `Publisher`:
+
+      class BankAccount extends Publisher {
+        private var balance = 0
+        def currentBalance: Int = balance        // <---
+        def deposit(amount: Int): Unit =
+          if amount > 0 then {
+            balance = balance + amount
+            publish()                            // <---
+          }
+        def withdraw(amount: Int): Unit =
+          if 0 < amount && amount <= balance then {
+            balance = balance - amount
+            publish()                            // <---
+          } else throw new Error("insufficient funds")
+      }
+
+
+An Observer
+===========
+
+A `Subscriber` to maintain the total balance of a list of accounts:
+
+      class Consolidator(observed: List[BankAccount]) extends Subscriber {
+        observed.foreach(_.subscribe(this))
+
+        private var total: Int = _
+        compute()
+
+        private def compute() =
+          total = observed.map(_.currentBalance).sum
+
+        def handler(pub: Publisher) = compute()
+
+        def totalBalance = total
+      }
+
+Observer Pattern, The Good
+==========================
+
+ - Decouples views from state
+ - Allows to have a varying number of views of a given state
+ - Simple to set up
+
+Observer Pattern, The Bad
+=========================
+
+ - Forces imperative style, since handlers are `Unit`-typed
+ - Many moving parts that need to be co-ordinated
+ - Concurrency makes things more complicated
+ - Views are still tightly bound to one state; view update happens immediately.
+
+To quantify (Adobe presentation from 2008):
+
+ - $1/3^{rd}$ of the code in Adobe's desktop applications is devoted to event handling.
+ - $1/2$ of the bugs are found in this code.
+
+
+How to Improve?
+===============
+
+During the rest of this week we will explore a different way, namely
+*functional reactive programming*, in which we can improve on the
+imperative view of reactive programming embodied in the observer pattern.
diff --git a/lectures/progfun2-3-5.md b/lectures/progfun2-3-5.md
new file mode 100644
index 0000000000000000000000000000000000000000..268f5355a0f58c689d68948323294211f6afc1e6
--- /dev/null
+++ b/lectures/progfun2-3-5.md
@@ -0,0 +1,221 @@
+% Functional Reactive Programming
+% Martin Odersky
+%
+What is FRP?
+============
+
+Reactive programming is about reacting to sequences of _events_ that happen in _time_.
+
+Functional view: Aggregate an event sequence into a _signal_.
+
+- A signal is a value that changes over time.
+
+- It is represented as a function from time to the value domain.
+
+- Instead of propagating updates to mutable state, we define new signals in terms of existing ones.
+
+Example: Mouse Positions
+========================
+
+\red{Event-based view:}
+
+Whenever the mouse moves, an event
+
+      MouseMoved(toPos: Position)
+
+is fired.
+
+\red{FRP view:}
+
+A signal,
+
+     mousePosition: Signal[Position]
+
+which at any point in time represents the current mouse position.
+
+
+Origins of FRP
+==============
+
+FRP started in 1997 with the paper [Functional Reactive Animation](http://conal.net/papers/icfp97/)
+by Conal Elliott and Paul Hudak and the [Fran](http://conal.net/fran/tutorial.htm) library.
+
+There have been many FRP systems since, both standalone languages and embedded libraries.
+
+Some examples are: [Flapjax](http://www.flapjax-lang.org/), [Elm](http://elm-lang.org/), [Bacon.js](https://baconjs.github.io/),
+[React4J](https://bitbucket.org/yann_caron/react4j/wiki/Home).
+
+Event streaming dataflow programming systems such as Rx (which we will see in two weeks),
+are related but the term FRP is not commonly used for them.
+
+We will introduce FRP by means of of a minimal class, `frp.Signal` whose implementation is explained at the end of this module.
+
+frp.Signal is modelled after Scala.react, which is described in the paper [Deprecating the Observer Pattern](http://infoscience.epfl.ch/record/176887/files/DeprecatingObservers2012.pdf).
+
+
+Fundamental Signal Operations
+=============================
+
+There are two fundamental operations over signals:
+
+$1.$ Obtain the value of the signal at the current time.\newline
+In our library this is expressed by `()` application.
+
+
+      mousePosition()   // the current mouse position
+
+->
+
+$2.$ Define a signal in terms of other signals.\newline
+\tab In our library, this is expressed by the `Signal` constructor.
+
+
+      def inReactangle(LL: Position, UR: Position): Signal[Boolean] =
+        Signal {
+          val pos = mousePosition()
+          LL <= pos && pos <= UR
+        }
+
+
+Constant Signals
+================
+
+The `Signal(...)` syntax can also be used to define a signal that has always the same
+value:
+
+      val sig = Signal(3)      // the signal that is always 3.
+
+Time-Varying Signals
+====================
+
+How do we define a signal that varies in time?
+
+ - We can use externally defined signals, such as `mousePosition`
+and map over them.
+
+ - Or we can use a `Var`.
+
+Variable Signals
+================
+
+Values of type `Signal` are immutable.
+
+But our library also defines a subclass `Var` of `Signal` for signals that
+can be changed.
+
+`Var` provides an "update" operation, which allows to redefine
+the value of a signal from the current time on.
+
+          val sig = Var(3)
+          sig.update(5)            // From now on, sig returns 5 instead of 3.
+
+Aside: Update Syntax
+====================
+
+In Scala, calls to `update` can be written as assignments.
+
+For instance, for an array `arr`
+
+     arr(i) = 0
+
+is translated to
+
+     arr.update(i, 0)
+
+which calls an `update` method which can be thought of as follows:
+
+     class Array[T] {
+       def update(idx: Int, value: T): Unit
+       ...
+     }
+
+Aside: Update Syntax
+====================
+
+Generally, an indexed assignment like
+$\btt     f(E_1, ..., E_n) = E$
+
+is translated to
+$\btt     f.update(E_1, ..., E_n, E)$.
+
+This works also if $n = 0$: $\btt f() = E$ is shorthand for $\btt f.update(E)$.
+
+Hence,
+
+      sig.update(5)
+
+can be abbreviated to
+
+      sig() = 5
+
+Signals and Variables
+=====================
+
+Signals of type `Var` look a bit like mutable variables, where
+
+      sig()
+
+is dereferencing, and
+
+      sig() = newValue
+
+is update.
+
+But there's a crucial difference:
+
+We can _map_ over signals, which gives us a relation between two signals that is maintained
+automatically, at all future points in time.
+
+No such mechanism exists for mutable variables; we have to propagate all updates manually.
+
+Example
+=======
+
+Repeat the `BankAccount` example of last section with signals.
+
+Add a signal `balance` to `BankAccount`s.
+
+Define a function `consolidated` which produces the sum of all balances
+of a given list of accounts.
+
+What savings were possible compared to the publish/subscribe implementation?
+
+Signals and Variables (2)
+=========================
+
+Note that there's an important difference between the variable assignment
+
+      v = v + 1
+
+and the signal update
+
+      s() = s() + 1
+
+In the first case, the _new_ value of `v` becomes the _old_ value of `v` plus 1.
+
+In the second case, we try define a signal `s` to be _at all points in time_ one larger than itself.
+
+This obviously makes no sense!
+
+
+Exercise
+========
+
+Consider the two code fragments below
+
+(1) 
+        val num = Signal(1)
+        val twice = Signal(num() * 2)
+        num() = 2
+      
+(2) 
+        var num = Signal(1)
+        val twice = Signal(num() * 2)
+        num = Signal(2)
+
+So they yield the same final value for `twice()`?
+
+      O   yes        
+      O   no
+        
+ 
\ No newline at end of file
diff --git a/lectures/progfun2-3-6.md b/lectures/progfun2-3-6.md
new file mode 100644
index 0000000000000000000000000000000000000000..479ba34fcd4edde8cd429a00bf2b797d040e3c2d
--- /dev/null
+++ b/lectures/progfun2-3-6.md
@@ -0,0 +1,285 @@
+% A Simple FRP Implementation
+% Martin Odersky
+%
+
+A Simple FRP Implementation
+===========================
+
+We now develop a simple implementation of `Signal`s and `Var`s, which together
+make up the basis of our approach to functional reactive programming.
+
+The classes are assumed to be in a package `frp`.
+
+Their user-facing APIs are summarized in the next slides.
+
+Summary: The Signal API
+=======================
+
+      class Signal[T](expr: => T) {
+        def apply(): T = ???
+      }
+      object Signal {
+        def apply[T](expr: => T) = new Signal(expr)
+      }
+
+Summary: The Var API
+====================
+
+      class Var[T](expr: => T) extends Signal[T](expr) {
+        def update(expr: => T): Unit = ???
+      }
+
+      object Var {
+        def apply[T](expr: => T) = new Var(expr)
+      }
+
+Implementation Idea
+===================
+
+Each signal maintains
+
+ - its current value,
+ - the current expression that defines the signal value,
+ - a set of _observers_: the other signals that depend on its value.
+
+Then, if the signal changes, all observers need to be re-evaluated.
+
+Dependency Maintenance
+======================
+
+How do we record dependencies in `observers`?
+
+ - When evaluating a signal-valued expression, need to know which
+   signal `caller` gets defined or updated by the expression.
+
+ - If we know that, then executing a `sig()` means adding `caller` to the
+   `observers` of `sig`.
+
+ - When signal `sig`'s value changes, all previously observing signals are
+   re-evaluated and the set `sig.observers` is cleared.
+
+ - Re-evaluation will re-enter a calling signal `caller` in `sig.observers`,
+   as long as `caller`'s value still depends on `sig`.
+
+Who's Calling?
+==============
+
+How do we find out on whose behalf a signal expression is evaluated?
+
+One simple (simplistic?) way to do this is to maintain a global data structure referring to
+the current caller. (We will discuss and refine this later).
+
+That data structure is accessed in a stack-like fashion because one evaluation of
+a signal might trigger others.
+
+Stackable Variables
+===================
+
+Here's a class for stackable variables:
+
+      class StackableVariable[T](init: T) {
+        private var values: List[T] = List(init)
+        def value: T = values.head
+        def withValue[R](newValue: T)(op: => R): R = {
+          values = newValue :: values
+          try op finally values = values.tail
+        }
+      }
+
+You access it like this
+
+      val caller = new StackableVar(initialSig)
+      caller.withValue(otherSig) { ... }
+      ... caller.value ...
+
+
+Set Up in Object `Signal`
+========================
+
+We also evaluate signal expressions at the top-level when there is no other signal
+that's defined or updated.
+
+We use the "sentinel" object `NoSignal` as the `caller` for these expressions.
+
+Together:
+
+      object NoSignal extends Signal[Nothing](???) { ... }
+
+      object Signal {
+        val caller = new StackableVariable[Signal[_]](NoSignal)
+        def apply[T](expr: => T) = new Signal(expr)
+      }
+
+The `Signal` Class
+==================
+
+      class Signal[T](expr: => T) {
+        import Signal._
+        private var myExpr: () => T = _
+        private var myValue: T = _
+        private var observers: Set[Signal[_]] = Set()
+        update(expr)
+->
+        protected def update(expr: => T): Unit = {
+          myExpr = () => expr
+          computeValue()
+        }
+->
+        protected def computeValue(): Unit = {
+          myValue = caller.withValue(this)(myExpr())
+        }
+
+The Signal Class, ctd
+=====================
+
+        def apply() = {
+          observers += caller.value
+          assert(!caller.value.observers.contains(this), "cyclic signal definition")
+          myValue
+        }
+      }
+
+Exercise
+========
+
+The `Signal` class still lacks an essential part. Which is it?
+
+       O     Error handling
+       O     Reevaluating callers
+       O     Constructing observers
+
+Reevaluating Callers
+====================
+
+A signal's current value can change when
+
+ - somebody calls an update operation on a `Var`, or
+ - the value of a dependent signal changes
+
+Propagating requires a more refined implementation of `computeValue`:
+
+      protected def computeValue(): Unit =
+        myValue = caller.withValue(this)(myExpr())
+
+Reevaluating Callers
+====================
+
+A signal's current value can change when
+
+ - somebody calls an update operation on a `Var`, or
+ - the value of a dependent signal changes
+
+Propagating changes requires a more refined implementation of `computeValue`:
+
+      protected def computeValue(): Unit = {
+        val newValue = caller.withValue(this)(myExpr())
+        if myValue != newValue then {
+          myValue = newValue
+          val obs = observers
+          observers = Set()
+          obs.foreach(_.computeValue())
+        }
+      }
+
+Handling NoSignal
+=================
+
+`computeValue` needs to be disabled for `NoSignal` because we cannot evaluate
+an expression of type `Nothing`:
+
+      object NoSignal extends Signal[Nothing](???) {
+        override def computeValue() = ()
+      }
+
+Handling `Var`s
+===============
+
+Recall that `Var` is a `Signal` which can be updated by the client program.
+
+In fact, all necessary functionality is already present in class `Signal`; we just need to expose it:
+
+      class Var[T](expr: => T) extends Signal[T](expr) {
+        override def update(expr: => T): Unit = super.update(expr)
+      }
+
+      object Var {
+        def apply[T](expr: => T) = new Var(expr)
+      }
+
+Discussion
+==========
+
+Our implementation of FRP is quite stunning in its simplicity.
+
+But you might argue that it is too simplistic.
+
+In particular, it makes use of the worst kind of state: global state.
+
+      object Signal {
+        val caller = new StackableVariable[Signal[_]](NoSignal)
+        ...
+      }
+
+One immediate problem is: What happens if we try to evaluate several signal
+expressions in parallel?
+->
+ - The caller signal will become "garbled" by concurrent updates.
+
+Thread-Local State
+==================
+
+One way to get around the problem of concurrent accesses to global state is to
+use synchronization.
+
+But this blocks threads, can be slow, and can lead to deadlocks.
+
+Another solution is to replace global state by thread-local state.
+
+ - Thread-local state means that each thread accesses a separate copy of a variable.
+
+ - It is supported in Scala through class `scala.util.DynamicVariable`.
+
+Using Thread-Local State
+========================
+
+The API of `DynamicVariable` matches the one of `StackableVariable`
+
+So we can simply swap it into our `Signal` implementation:
+
+      object Signal {
+        val caller = new DynamicVariable[Signal[_]](NoSignal)
+        ...
+      }
+
+Another Solution: Implicit Parameters
+=====================================
+
+Thread-local state still comes with a number of disadvantages:
+
+ - Its imperative nature often produces hidden dependencies which are hard to manage.
+ - Its implementation on the JDK involves a global hash table lookup, which can be a performance problem.
+ - It does not play well in situations where threads are multiplexed between several tasks.
+->
+A cleaner solution involves implicit parameters.
+
+ - Instead of maintaining a thread-local variable, pass its current value into
+   a signal expression as an implicit parameter.
+ - This is purely functional. But it currently requires more boilerplate than the thread-local solution.
+ - Future versions of Scala might solve that problem.
+
+Summary
+=======
+
+We have given a quick tour of functional reactive programming,
+with some usage examples and an implementation.
+
+This is just a taster, there's much more to be discovered.
+
+In particular, we only covered one particular style of FRP: Discrete
+signals changed by events.
+
+Some variants of FRP also treat continuous signals.
+
+Values in these systems are often computed by sampling instead of
+event propagation.
+
diff --git a/lectures/progfun2-4-1.md b/lectures/progfun2-4-1.md
new file mode 100644
index 0000000000000000000000000000000000000000..7a04b63e834c9a3203a1d55d996709f5f42c14d2
--- /dev/null
+++ b/lectures/progfun2-4-1.md
@@ -0,0 +1,127 @@
+% Implicit Programming — Motivating Example
+%
+%
+
+Sorting Lists of Numbers
+========================
+
+Consider a method `sort` that takes as parameter a `List[Int]` and
+returns another `List[Int]` containing the same elements, but sorted:
+
+~~~
+def sort(xs: List[Int]): List[Int] = {
+  ...
+  ... if x < y then ...
+  ...
+}
+~~~
+
+At some point, this method has to compare two elements `x` and `y`
+of the given list.
+
+Making `sort` more General
+==========================
+
+Problem: How to parameterize `sort` so that it can also be
+used for lists with elements other than `Int`, such as `Double`
+or `String`?
+
+A straightforward approach would be to use a polymorphic type
+`A` for the type of elements:
+
+~~~
+def sort[A](xs: List[A]): List[A] = ...
+~~~
+
+But this does not work, because the comparison `<` is not defined for
+all arbitrary types `A`.
+
+Parameterization of `sort`
+==========================
+
+The most flexible design is to pass the comparison operation
+as an additional parameter:
+
+~~~
+def sort[A](xs: List[A])(lessThan: (A, A) => Boolean): List[A] = {
+  ...
+  ... if lessThan(x, y) then ...
+  ...
+}
+~~~
+
+Calling Parameterized `sort`
+============================
+
+We can now call `sort` as follows:
+
+~~~
+scala> val xs = List(-5, 6, 3, 2, 7)
+scala> val strings = List("apple", "pear", "orange", "pineapple")
+
+scala> sort(xs)((x, y) => x < y)
+res0: List[Int] = List(-5, 2, 3, 6, 7)
+
+scala> sort(strings)((f1, f2) => f1.compareTo(f2) < 0)
+res1: List[String] = List(apple, orange, pear, pineapple)
+~~~
+
+Parameterization with Ordering
+==============================
+
+There is already a class in the standard library that represents orderings:
+
+~~~
+scala.math.Ordering[A]
+~~~
+
+Provides ways to compare elements of type `A`. So, instead of
+parameterizing with the `lessThan` function, we could parameterize
+with `Ordering` instead:
+
+~~~
+def sort[A](xs: List[A])(ord: Ordering[A]): List[A] = {
+  ...
+  ... if ord.lt(x, y) then ...
+  ...
+}
+~~~
+
+Ordering Instances
+==================
+
+Calling the new `sort` can be done like this:
+
+~~~
+import scala.math.Ordering
+
+sort(xs)(Ordering.Int)
+sort(strings)(Ordering.String)
+~~~
+
+This makes use of the values `Int` and `String` defined in the
+`scala.math.Ordering` object, which produce the right
+orderings on integers and strings.
+
+~~~
+object Ordering {
+  val Int = new Ordering[Int] {
+    def lt(x: Int, y: Int) = x - y < 0
+  }
+}
+~~~
+
+Reducing Boilerplate
+====================
+
+Problem: Passing around `Ordering` values is cumbersome.
+
+~~~
+sort(xs)(Ordering.Int)
+sort(ys)(Ordering.Int)
+sort(strings)(Ordering.String)
+~~~
+
+Sorting a `List[Int]` instance always uses the same `Ordering.Int` value,
+sorting a `List[String]` instance always uses the same `Ordering.String`
+value, and so on…
diff --git a/lectures/progfun2-4-2.md b/lectures/progfun2-4-2.md
new file mode 100644
index 0000000000000000000000000000000000000000..0013ab1f437cd394ef0de549428548393084497f
--- /dev/null
+++ b/lectures/progfun2-4-2.md
@@ -0,0 +1,255 @@
+% Implicits
+%
+%
+
+Reminder: General `sort` Operation
+==================================
+
+~~~
+def sort[A](xs: List[A])(ord: Ordering[A]): List[A] = ...
+~~~
+
+Problem: Passing around `Ordering` values is cumbersome.
+
+~~~
+sort(xs)(Ordering.Int)
+sort(ys)(Ordering.Int)
+sort(strings)(Ordering.String)
+~~~
+
+Sorting a `List[Int]` instance always uses the same `Ordering.Int` value,
+sorting a `List[String]` instance always uses the same `Ordering.String`
+value, and so on…
+
+Implicit Parameters
+===================
+
+We can reduce the boilerplate by making `ord` an **implicit** parameter.
+
+~~~
+def sort[A](xs: List[A])(implicit ord: Ordering[A]): List[A] = ...
+~~~
+
+- A method can have only one implicit parameter list, and it must be the last
+  parameter list given.
+
+Then calls to `sort` can omit the `ord` parameter:
+
+~~~
+sort(xs)
+sort(ys)
+sort(strings)
+~~~
+
+The compiler infers the implicit parameter value based on the
+**queried type**.
+
+Implicit Parameters (2)
+=======================
+
+~~~
+def sort[A](xs: List[A])(implicit ord: Ordering[A]): List[A] = ...
+
+val xs: List[Int] = ...
+~~~
+
+->
+
+~~~
+sort(xs)
+~~~
+
+->
+
+~~~
+sort[Int](xs)
+~~~
+
+->
+
+~~~
+sort[Int](xs)(Ordering.Int)
+~~~
+
+In this case, the queried type is `Ordering[Int]`.
+
+Implicit Parameters Resolution
+==============================
+
+Say, a function takes an implicit parameter of type `T`.
+
+The compiler will search an implicit **definition** that:
+
+- is marked `implicit`,
+- has a type compatible with `T`,
+- is visible at the point of the function call, or is defined
+  in a companion object associated with `T`.
+
+If there is a single (most specific) definition, it will be taken
+as actual arguments for the implicit parameter.
+
+Otherwise it’s an error.
+
+Implicit Definitions
+====================
+
+For the previous example to work, the `Ordering.Int` value definition
+must be marked `implicit`:
+
+~~~
+object Ordering {
+
+  implicit val Int: Ordering[Int] = ...
+
+}
+~~~
+
+Implicit Search
+===============
+
+The implicit search for a type `T` includes:
+
+- all the implicit definitions that are visible (inherited, imported,
+  or defined in an enclosing scope),
+- the *implicit scope* of type `T`, made of implicit definitions found
+  in a companion object *associated* with `T`. In essence$^*$, the types
+  associated with a type `T` are:
+    - if `T` is a compound type $T_1 with T_2 ... with T_n$, the union
+      of the parts of $T_1$, ... $T_n$ as well as $T$ itself,
+    - if `T` is a parameterized type $S[T_1, T_2, ..., T_n]$, the union
+      of the parts of $S$ and $T_1$, ..., $T_n$,
+    - otherwise, just `T` itself.
+
+In the case of the `sort(xs)` call, the compiler looks for an implicit
+`Ordering[Int]` definition, which is found in the `Ordering` companion
+object.
+
+Implicit Not Found
+==================
+
+If there is no available implicit definition matching the queried type,
+an error is reported:
+
+~~~
+scala> def f(implicit n: Int) = ()
+scala> f
+       ^
+error: could not find implicit value for parameter n: Int
+~~~
+
+Ambiguous Implicit Definitions
+==============================
+
+If more than one implicit definition are eligible, an **ambiguity** is reported:
+
+~~~
+scala> implicit val x: Int = 0
+scala> implicit val y: Int = 1
+scala> def f(implicit n: Int) = ()
+scala> f
+       ^
+error: ambiguous implicit values:
+ both value x of type => Int
+ and value y of type => Int
+ match expected type Int
+~~~
+
+Priorities
+==========
+
+Actually, several implicit definitions matching the same type don’t generate an
+ambiguity if one is **more specific** than the other.
+
+In essence$^{*}$, a definition `a: A` is more specific than a definition `b: B` if:
+
+- type `A` is a subtype of type `B`,
+- type `A` has more “fixed” parts,
+- `a` is defined in a class or object which is a subclass of the class defining `b`.
+
+Priorities: Example (1)
+=======================
+
+Which implicit definition matches the queried `Int` implicit parameter when
+the `f` method is called?
+
+~~~
+implicit def universal[A]: A = ???
+implicit def int: Int = ???
+
+def f(implicit n: Int) = ()
+
+f
+~~~
+
+Priorities: Example (2)
+=======================
+
+Which implicit definition matches the queried `Int` implicit parameter when
+the `f` method is called?
+
+~~~
+trait A {
+  implicit val x: Int = 0
+}
+trait B extends A {
+  implicit val y: Int = 1
+  
+  def f(implicit n: Int) = ()
+  
+  f
+}
+~~~
+
+Context Bounds
+==============
+
+A syntactic sugar allows the omission of the implicit parameter list:
+
+~~~
+def printSorted[A: Ordering](as: List[A]): Unit = {
+  println(sort(as))
+}
+~~~
+
+Type parameter `A` has one **context bound**: `Ordering`. There must be an
+implicit value with type `Ordering[A]` at the point of application.
+
+More generally, a method definition such as:
+
+$def f[A: U_1 ... : U_n](ps): R = ...$
+
+Is expanded to:
+
+$def f[A](ps)(implicit ev_1: U_1[A], ..., ev_n: U_n[A]): R = ...$
+
+Implicit Query
+==============
+
+At any point in a program, one can **query** an implicit value of
+a given type by calling the `implicitly` operation:
+
+~~~
+scala> implicitly[Ordering[Int]]
+res0: Ordering[Int] = scala.math.Ordering$Int$@73564ab0
+~~~
+
+`implicitly` is not a special keyword, it is defined as a library operation:
+
+~~~
+def implicitly[A](implicit value: A): A = value
+~~~
+
+Summary
+=======
+
+In this lecture we have introduced the concept of **implicit programming**,
+a language mechanism that infers **values** by using **type** information.
+
+There has to be a **unique** implicit definition matching the queried type
+for it to be used by the compiler.
+
+Implicit values are searched in the enclosing **lexical scope** (imports,
+parameters, inherited members) as well as in the **implicit scope** made
+of implicits defined in companion objects of types associated with the
+queried type.
+
diff --git a/lectures/progfun2-4-3.md b/lectures/progfun2-4-3.md
new file mode 100644
index 0000000000000000000000000000000000000000..ed712bcc118ca09ab341a9079c6a55d73e17b5b4
--- /dev/null
+++ b/lectures/progfun2-4-3.md
@@ -0,0 +1,173 @@
+% Type Classes vs Inheritance
+%
+%
+
+Type Classes
+============
+
+In the previous lectures we have seen a particular pattern of code
+to achieve *ad hoc* polymorphism:
+
+~~~
+trait Ordering[A] { def lt(a1: A, a2: A): Boolean }
+
+object Ordering {
+  implicit val Int: Ordering[Int] = (x, y) => x < y
+  implicit val String: Ordering[String] = (s, t) => (s compareTo t) < 0
+}
+
+def sort[A: Ordering](xs: List[A]): List[A] = ...
+~~~
+
+We say that `Ordering` is a **type class**.
+
+Alternatively, Using *Subtyping Polymorphism*
+=============================================
+
+~~~
+trait Ordered {
+  def lt(other: Ordered): Boolean
+}
+~~~
+
+~~~
+def sort2(xs: List[Ordered]): List[Ordered] = {
+  ...
+  ... if x lt y then ...
+  ...
+}
+~~~
+
+How do these approaches compare?
+
+Usage
+=====
+
+(Assuming that `Int <: Ordered`)
+
+~~~
+val sortedInts: List[Int] = sort2(ints)
+~~~
+
+->
+
+~~~
+                            ^^^^^^^^^^^
+error: type mismatch;
+ found   : List[Ordered]
+ required: List[Int]
+~~~
+
+->
+
+~~~
+def sort2(xs: List[Ordered]): List[Ordered]
+~~~
+
+First Improvement
+=================
+
+~~~
+def sort2[A <: Ordered](xs: List[A]): List[A] = // ... same implementation
+~~~
+
+->
+
+~~~
+val sortedInts:   List[Int]    = sort2(ints)    // OK
+val sortedString: List[String] = sort2(strings) // OK
+~~~
+
+... assuming `Int <: Ordered` and `String <: Ordered`!
+
+Subtyping Polymorphism Causes Strong Coupling
+=============================================
+
+Subtyping polymorphism imposes a strong coupling between *operations*
+(`Ordered`) and *data types* (`Int`, `String`) that support them.
+
+By contrast, type classes encourage separating the definition of data types
+and the operations they support: the `Ordering[Int]` and `Ordering[String]`
+instances don’t have to be defined in the same unit as the `Int` and `String`
+data types.
+
+Type classes support *retroactive* extension: the ability to extend a data
+type with new operations without changing the original definition of the data type.
+
+Implementing an Operation for a Custom Data Type
+================================================
+
+~~~
+case class Rational(num: Int, denom: Int)
+~~~
+
+->
+
+~~~
+case class Rational(num: Int, denom: Int) extends Ordered {
+  def lt(other: Ordered) = other match {
+    case Rational(otherNum, otherDenom) => num * otherDenom < otherNum * denom
+    case _ => ???
+  }
+}
+~~~
+
+->
+
+We want to compare rational numbers with other rational numbers only!
+
+Second Improvement: F-Bounded Polymorphism
+==========================================
+
+~~~
+trait Ordered[This <: Ordered[This]] {
+  def lt(other: This): Boolean
+}
+
+case class Rational(num: Int, denom: Int) extends Ordered[Rational] {
+  def lt(other: Rational) = num * other.denom < other.num * denom
+}
+
+def sort2[A <: Ordered[A]](xs: List[A]): List[A] = {
+  ...
+  ... if x lt y then ...
+  ...
+}
+~~~
+
+Implementing an Operation for a Custom Data Type (2)
+====================================================
+
+~~~
+case class Rational(num: Int, denom: Int)
+
+object Rational {
+  implicit val ordering: Ordering[Rational] =
+    (x, y) => x.num * y.denom < y.num * x.denom
+}
+
+sort(Rational(1, 2), Rational(1, 3))
+~~~
+
+->
+
+(The `Ordering[Rational]` instance definition could be in a different project)
+
+Dispatch Time
+=============
+
+Another difference between subtyping polymorphism and type classes is **when**
+the dispatch happens.
+
+- With type classes, the implicit instance is resolved at **compilation time**,
+- With subtyping polymorphism, the actual implementation of a method is resolved
+  at **run time**.
+
+Summary
+=======
+
+In this lecture we have seen that type classes support retroactive
+extensibility.
+
+The dispatch happens at compile time with type classes, whereas it
+happens at run time with subtype polymorphism.
diff --git a/lectures/progfun2-4-4-type-level.md b/lectures/progfun2-4-4-type-level.md
new file mode 100644
index 0000000000000000000000000000000000000000..e462024fefdca802a3b285e262b1418b0c18167d
--- /dev/null
+++ b/lectures/progfun2-4-4-type-level.md
@@ -0,0 +1,203 @@
+% Type-Level Programming
+%
+%
+
+Inductive Implicits (1)
+=======================
+
+An arbitrary number of implicit definitions can be combined
+until the search hits a “terminal” definition:
+
+~~~
+implicit def a: A = ...
+implicit def aToB(implicit a: A): B = ...
+implicit def bToC(implicit b: B): C = ...
+implicit def cToD(implicit c: C): D = ...
+
+implicitly[D]
+~~~
+
+Inductive Implicits (2)
+=======================
+
+~~~
+trait Nat
+trait Z extends Nat
+trait S[N <: Nat] extends Nat
+~~~
+
+~~~
+implicit def zero: Z = null
+implicit def one  (implicit zero: Z):      S[Z]       = null
+implicit def two  (implicit one: S[Z]):    S[S[Z]]    = null
+implicit def three(implicit two: S[S[Z]]): S[S[S[Z]]] = null
+
+implicitly[S[S[S[Z]]]]
+~~~
+
+Inductive Implicits (3)
+=======================
+
+~~~
+trait Nat
+trait Z extends Nat
+trait S[N <: Nat] extends Nat
+~~~
+
+~~~
+implicit def zero: Z = null
+implicit def succ[N <: Nat](implicit n: N): S[N] = null
+
+implicitly[S[S[S[Z]]]]
+~~~
+
+Recursive Implicits
+===================
+
+~~~
+trait A
+implicit def loop(implicit a: A): A = a
+
+implicitly[A]
+~~~
+
+->
+
+~~~
+          ^
+error: diverging implicit expansion for type A
+starting with method loop
+~~~
+
+Computing Types
+===============
+
+~~~
+trait Water
+trait Ice
+
+trait Melted[A, R]
+
+object Melted {
+  implicit def meltedIce: Melted[Ice, Water] = null
+}
+
+def meltedIce[R](implicit m: Melted[Ice, R]): R = ???
+~~~
+
+~~~
+> def water = meltedIce
+water: Water
+~~~
+
+From Types to Values (1)
+========================
+
+~~~
+type `0` = Z
+type `1` = S[`0`]
+type `2` = S[`1`]
+type `3` = S[`2`]
+...
+~~~
+
+~~~
+val x: `2` = `2`
+             ^
+error: not found: value `2`
+~~~
+
+From Types to Values (2)
+========================
+
+~~~
+case class ValueOf[N <: Nat] private (get: Int)
+
+object ValueOf {
+
+  implicit def base: ValueOf[Z] = ValueOf(0)
+  
+  implicit def induc[A <: Nat](implicit h: ValueOf[A]): ValueOf[S[A]] =
+    ValueOf(h.get + 1)
+
+}
+~~~
+
+~~~
+> implicitly[ValueOf[`3`]].get
+res0: Int = 3
+~~~
+
+Example: Sized Collections (1)
+==============================
+
+~~~
+case class Sized[N <: Nat] private (elems: Seq[Int]) {
+
+  def + (other: Sized[N]): Sized[N] =
+    Sized(elems.zip(other.elems).map { case (x, y) => x + y })
+
+}
+~~~
+
+~~~
+def usage(xs: Sized[`3`], ys: Sized[`3`], zs: Sized[`4`]) = {
+  xs + ys // OK
+  xs + zs // Error: type mismatch;
+          //         found   : Sized[`4`]
+          //         required: Sized[`3`]
+}
+~~~
+
+Example: Sized Collections (2)
+==============================
+
+~~~
+case class Sized[N <: Nat] private (elems: Seq[Int]) {
+
+  def concat[M <: Nat](other: Sized[M]): Sized[???]
+
+}
+~~~
+
+Example: Sized Collections (3)
+==============================
+
+~~~
+case class Sized[N <: Nat] private (elems: Seq[Int]) {
+
+  def concat[M <: Nat, S <: Nat](
+    other: Sized[M]
+  )(implicit
+    sum: Sum[N, M, S]
+  ): Sized[S] =
+    Sized(elems ++ other.elems)
+
+}
+~~~
+
+Example: Sized Collections (4)
+==============================
+
+~~~
+trait Sum[A <: Nat, B <: Nat, R <: Nat]
+
+object Sum {
+
+  implicit def base[B <: Nat]: Sum[Z, B, B] = null
+  
+  implicit def induc[A <: Nat, B <: Nat, R <: Nat](implicit
+    h: Sum[A, B, R]
+  ): Sum[S[A], B, S[R]] = null
+
+}
+~~~
+
+Summary
+=======
+
+In this video, we have seen:
+
+- implicit definitions can be **inductive**
+- types can be computed according to the result of the implicit
+  search
diff --git a/lectures/progfun2-4-4.md b/lectures/progfun2-4-4.md
new file mode 100644
index 0000000000000000000000000000000000000000..aea70840a2605bba1ab2a71f7c8130f1b7c101b1
--- /dev/null
+++ b/lectures/progfun2-4-4.md
@@ -0,0 +1,118 @@
+% Higher-Order Implicits
+%
+%
+
+Higher-Order Implicits (1)
+==========================
+
+Consider how we order two `String` values:
+
+- `"abc" < "abd"`?
+
+\vspace{3cm}
+
+-> We compare the characters of each string, element-wise.
+
+-> **Problem**: How to generalize this process to sequences of any
+   element type `A` for which there is an implicit `Ordering[A]`
+   instance?
+
+Higher-Order Implicits (2)
+==========================
+
+~~~
+implicit def listOrdering[A](implicit
+  ord: Ordering[A]
+): Ordering[List[A]] = ...
+~~~
+
+Higher-Order Implicits (3)
+==========================
+
+~~~
+implicit def listOrdering[A](implicit
+  ord: Ordering[A]
+): Ordering[List[A]] = { (xs0, ys0) =>
+  def loop(xs: List[A], ys: List[A]): Boolean = (xs, ys) match {
+    case (x :: xsTail, y :: ysTail) => ord.lt(x, y) && loop(xsTail, ysTail)
+    case (xs, ys) => ys.nonEmpty
+  }
+  loop(xs0, ys0)
+}
+~~~
+
+~~~
+scala> sort(List(List(1, 2, 3), List(1), List(1, 1, 3)))
+res0: List[List[Int]] = List(List(1), List(1, 1, 3), List(1, 2, 3))
+~~~
+
+Higher-Order Implicits (4)
+==========================
+
+~~~
+def sort[A](xs: List[A])(implicit ord: Ordering[A]): List[A]
+implicit def listOrdering[A](implicit ord: Ordering[A]): Ordering[List[A]]
+
+val xss: List[List[Int]] = ...
+sort(xss)
+~~~
+
+->
+
+~~~
+sort[List[Int]](xss)
+~~~
+
+->
+
+~~~
+sort[List[Int]](xss)(listOrdering)
+~~~
+
+->
+
+~~~
+sort[List[Int]](xss)(listOrdering(Ordering.Int))
+~~~
+
+Higher-Order Implicits (5)
+==========================
+
+An arbitrary number of implicit definitions can be combined
+until the search hits a “terminal” definition:
+
+~~~
+implicit def a: A = ...
+implicit def aToB(implicit a: A): B = ...
+implicit def bToC(implicit b: B): C = ...
+implicit def cToD(implicit c: C): D = ...
+
+implicitly[D]
+~~~
+
+Recursive Implicits
+===================
+
+~~~
+trait A
+implicit def loop(implicit a: A): A = a
+
+implicitly[A]
+~~~
+
+->
+
+~~~
+          ^
+error: diverging implicit expansion for type A
+starting with method loop
+~~~
+
+Summary
+=======
+
+In this lecture, we have seen:
+
+- implicit definitions can also take implicit parameters
+- an arbitrary number of implicitly definitions can be chained
+  until a terminal definition is reached
diff --git a/lectures/progfun2-4-5.md b/lectures/progfun2-4-5.md
new file mode 100644
index 0000000000000000000000000000000000000000..193b441c9979c9f8e3af358f6ff75f41478c9cae
--- /dev/null
+++ b/lectures/progfun2-4-5.md
@@ -0,0 +1,219 @@
+% Implicit Conversions
+%
+%
+
+Implicit Conversions
+====================
+
+The last implicit-related mechanism of the language is implicit
+**conversions**.
+
+They make it possible to convert an expression to a different
+type.
+
+This mechanism is usually used to provide more ergonomic APIs:
+
+~~~
+// { "name": "Paul", "age": 42 }
+Json.obj("name" -> "Paul", "age" -> 42)
+
+
+val delay = 15.seconds
+~~~
+
+Type Coercion: Motivation (1)
+=============================
+
+~~~
+sealed trait Json
+case class JNumber(value: BigDecimal) extends Json
+case class JString(value: String) extends Json
+case class JBoolean(value: Boolean) extends Json
+case class JArray(elems: List[Json]) extends Json
+case class JObject(fields: (String, Json)*) extends Json
+~~~
+
+->
+
+~~~
+// { "name": "Paul", "age": 42 }
+JObject("name" -> JString("Paul"), "age" -> JNumber(42))
+~~~
+
+Problem: Constructing JSON objects is too verbose.
+
+Type Coercion: Motivation (2)
+=============================
+
+~~~
+sealed trait Json
+case class JNumber(value: BigDecimal) extends Json
+case class JString(value: String) extends Json
+case class JBoolean(value: Boolean) extends Json
+case class JArray(elems: List[Json]) extends Json
+case class JObject(fields: (String, Json)*) extends Json
+~~~
+
+~~~
+// { "name": "Paul", "age": 42 }
+Json.obj("name" -> "Paul", "age" -> 42)
+~~~
+
+How could we support the above user-facing syntax?
+
+Type Coercion: Motivation (3)
+=============================
+
+~~~
+// { "name": "Paul", "age": 42 }
+Json.obj("name" -> "Paul", "age" -> 42)
+~~~
+
+What could be the type signature of the `obj` constructor?
+
+->
+
+~~~
+def obj(fields: (String, Any)*): Json
+~~~
+
+->
+
+Allows invalid JSON objects to be constructed!
+
+~~~
+Json.obj("name" -> ((x: Int) => x + 1))
+~~~
+
+We want invalid code to be signaled to the programmer with a
+compilation error.
+
+Type Coercion (1)
+=================
+
+~~~
+object Json {
+
+  def obj(fields: (String, JsonField)*): Json =
+    JObject(fields.map(_.toJson): _*)
+    
+  trait JsonField {
+    def toJson: Json
+  }
+
+}
+~~~
+
+Type Coercion (2)
+=================
+
+~~~  
+trait JsonField {
+  def toJson: Json
+}
+
+object JsonField {
+  implicit def stringToJsonField(s: String): JsonField = () => JString(s)
+  implicit def intToJsonField(n: Int): JsonField = () => JNumber(n)
+  ...
+  implicit def jsonToJsonField(j: Json): JsonField = () => j
+}
+~~~
+
+Type Coercion: Usage
+====================
+
+~~~
+Json.obj("name" -> "Paul", "age" -> 42)
+~~~
+
+->
+
+The compiler implicitly inserts the following conversions:
+
+~~~
+Json.obj(
+  "name" -> Json.JsonField.stringToJsonField("Paul"),
+  "age" -> Json.JsonField.intToJsonField(42)
+)
+~~~
+
+Extension Methods: Motivation (1)
+=================================
+
+~~~
+case class Duration(value: Int, unit: TimeUnit)
+~~~
+
+->
+
+~~~
+val delay = Duration(15, TimeUnit.Second)
+~~~
+
+Extension Methods: Motivation (2)
+=================================
+
+~~~
+case class Duration(value: Int, unit: TimeUnit)
+~~~
+
+~~~
+val delay = 15.seconds
+~~~
+
+How could we support the above user-facing syntax?
+
+Extension Methods
+=================
+
+~~~
+case class Duration(value: Int, unit: TimeUnit)
+
+object Duration {
+
+  object Syntax {
+    implicit def hasSeconds(n: Int): HasSeconds = new HasSeconds(n)
+  }
+  
+  class HasSeconds(n: Int) {
+    def seconds: Duration = Duration(n, TimeUnit.Second)
+  }
+
+}
+~~~
+
+Extension Methods: Usage
+========================
+
+~~~
+import Duration.Syntax._
+
+val delay = 15.seconds
+~~~
+
+->
+
+The compiler implicitly inserts the following conversion:
+
+~~~
+val delay = hasSeconds(15).seconds
+~~~
+
+Implicit Conversions
+====================
+
+The compiler looks for implicit conversions on an expression `e` of type `T`
+in the following situations:
+
+- `T` does not conform to the expression’s expected type,
+- in a selection `e.m`, if member `m` is not accessible on `T`,
+- in a selection `e.m(args)`, if member `m` is accessible on `T` but is not
+  applicable to the arguments `args`.
+
+Note: at most one implicit conversion can be applied to a given expression.
+
+Summary
+=======
+
+- Implicit conversions can improve the ergonomics of an API