diff --git a/notebooks/08_Functors+Lambdas.ipynb b/notebooks/08_Functors+Lambdas.ipynb
index f0c89ded37556db41667ac172757a94fe5e6f42d..9b38def464e0659c75086e543e7b1f4976315afb 100644
--- a/notebooks/08_Functors+Lambdas.ipynb
+++ b/notebooks/08_Functors+Lambdas.ipynb
@@ -8,15 +8,6 @@
     "# Functors, Lambdas and std::function"
    ]
   },
-  {
-   "cell_type": "markdown",
-   "id": "43efc656",
-   "metadata": {},
-   "source": [
-    "    \n",
-    "   "
-   ]
-  },
   {
    "cell_type": "markdown",
    "id": "008aa425",
@@ -222,7 +213,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 8,
    "id": "856ee747",
    "metadata": {},
    "outputs": [
@@ -278,7 +269,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 9,
    "id": "4adb7f0b",
    "metadata": {},
    "outputs": [],
@@ -288,7 +279,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 10,
    "id": "f9bfde7f",
    "metadata": {},
    "outputs": [
@@ -409,12 +400,12 @@
    "metadata": {},
    "source": [
     "##### **Example 5:**\n",
-    "Now lets use a functor with a slightly more complicated interface"
+    "Now let us use a functor with a slightly more complicated interface"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 20,
    "id": "ff8d558b-da83-46d9-9842-8e0d9d5ba56d",
    "metadata": {},
    "outputs": [],
@@ -427,7 +418,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 21,
    "id": "b5790b96-bf62-4d6e-ad9c-e56839882728",
    "metadata": {},
    "outputs": [
@@ -437,7 +428,7 @@
        "6"
       ]
      },
-     "execution_count": 16,
+     "execution_count": 21,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -465,7 +456,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 22,
    "id": "646b302f-cdf5-40ed-98f8-01d98a9b277a",
    "metadata": {},
    "outputs": [],
@@ -482,7 +473,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 26,
    "id": "b56b370a-654f-4665-a86d-1b4a1497a813",
    "metadata": {},
    "outputs": [],
@@ -492,7 +483,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 27,
    "id": "b267e62f",
    "metadata": {},
    "outputs": [
@@ -519,13 +510,618 @@
     "#### Lambdas\n",
     "Using *function pointers* or *functors* implies some significant coding overhead.\n",
     "\n",
-    "C++11 introduced **lambda (functions)**, also known as **closures** as a leight-weight alternative, that can be written inline in the source code."
+    "C++11 introduced **lambda (functions)**, also known as **closures**, as a leight-weight alternative, that can be written inline in the source code."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "41bbe5a3",
+   "metadata": {},
+   "source": [
+    "Let's start with an example"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 43,
+   "id": "24ad0ef1",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      " 1 4 9 16 25"
+     ]
+    }
+   ],
+   "source": [
+    "#include <vector>\n",
+    "#include <iostream>\n",
+    "#include <algorithm>\n",
+    "\n",
+    "std::vector< int > numbers{1,2,3,4,5};\n",
+    "\n",
+    "for_each( numbers.begin(), numbers.end(), []( int& n ) { n *= n; } );\n",
+    "\n",
+    "for( int v: numbers ) std::cout << \" \" << v;"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ff3d112c",
+   "metadata": {},
+   "source": [
+    "The expression **`[]( int& n ) { n *= n; }`** is an *anonymous*, so called, lambda, lambda expression, lambda function or closure.\n",
+    "\n",
+    "- The `(...)` part specifies the interface.\n",
+    "- If a lambda function does not return a value we can omit the return type.\n",
+    "- The `{...}` part is for the body of the function.\n",
+    "- The `[...]` part allows to *capture* variables from the current scope.\n",
+    "\n",
+    "lambda functions do not really provide extra functionality. We could accomplish the same with a free-function or a functor. In this sense they are only syntactic sugar, \"but of the sweetest kind\" (Rainer Grimm)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5764a9c9",
+   "metadata": {},
+   "source": [
+    "##### Captures:\n",
+    "\n",
+    "The `[ ]` part of the lambda expression can contain a comma-separated list of stuff we want to *capture*. This allows using it inside the body of the lambda without explicitely passing it through the interface. There are also two *defaults* we can speficy:\n",
+    "\n",
+    "|       | effect |\n",
+    "|:-----:|:-------|\n",
+    "| &     | implicitly capture the used automatic variables by reference |\n",
+    "| :     | implicitly capture the used automatic variables by copy      |\n",
+    "| a     | capture a from surrounding scope by copy                     |\n",
+    "| &a    | capture a from surrounding scope by reference                |\n",
+    "\n",
+    "See e.g. <a href=\"https://en.cppreference.com/w/cpp/language/lambda#Lambda_capture\">cppreference.com</a> for a full list of possibilities."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "645373e5",
+   "metadata": {},
+   "source": [
+    "We are going to take a look at three examples from <a href=\"https://www.grimm-jaud.de/index.php/modernes-c-in-der-praxis-linux-magazin-a\">Modernes C++ in der Praxis</a> by Rainer Grimm."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "3514f84c",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <iostream>\n",
+    "\n",
+    "int main() {\n",
+    "\n",
+    "  std::cout << std::endl;\n",
+    "\n",
+    "  std::string copy = \"original\";\n",
+    "  std::string ref  = \"original\";\n",
+    "\n",
+    "  auto lambda = [copy,&ref] { std::cout << copy << \" \" << ref << std::endl; };\n",
+    "\n",
+    "  lambda();\n",
+    "\n",
+    "  copy = \"changed\";\n",
+    "  ref  = \"changed\";\n",
+    "\n",
+    "  lambda();\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "7bca56ac",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "\n",
+      "original original\n",
+      "original changed\n"
+     ]
+    }
+   ],
+   "source": [
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b237f560",
+   "metadata": {},
+   "source": [
+    "In line #10 we have our lambda expression.\n",
+    "- It does not receive arguments through its interface. Thus, we can omit the `( )` part.\n",
+    "- When we want to re-use a lambda we can store it in a variable. For this we need to use `auto`, asSmalleSmalle.\n",
+    "- Note the difference in the output between the two invocations of `lambda()`. As we captured `copy` by copy its value inside `lambda()` is not affected by its change in the surrounding scope, while that of `ref` is. The latter was captured by reference."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ac5c13c5",
+   "metadata": {},
+   "source": [
+    "***\n",
+    "Small detour: Coming back to what the type is. Let's try to figure out using `typeid`:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "f0fd0543",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "5.0 is of type d\n",
+      "type of a is i\n",
+      "What is lambda? ZN11__cling_N5415__cling_Un1Qu33EPvE3$_1\n"
+     ]
+    }
+   ],
+   "source": [
+    "std::cout << \"5.0 is of type \" << typeid(5.0).name() << std::endl;\n",
+    "int a;\n",
+    "std::cout << \"type of a is \" << typeid(a).name() << std::endl;\n",
+    "\n",
+    "\n",
+    "auto lambda = [] (int a) { std::cout << a << std::endl; };\n",
+    "std::cout << \"What is lambda? \" << typeid(lambda).name() << std::endl;"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3393bdde",
+   "metadata": {},
+   "source": [
+    "Not very helpful, hugh. So maybe let's try something else"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 61,
+   "id": "a32b1635",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Overwriting lambda.cpp\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%file lambda.cpp\n",
+    "\n",
+    "void test() {\n",
+    "\n",
+    "auto lambda = [] (int &a) { a++; };\n",
+    "\n",
+    "int b = 1;\n",
+    "lambda(b); // avoid optimizing out\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 62,
+   "id": "85373b9f",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!g++ -c lambda.cpp"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 63,
+   "id": "fa038d35",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "                 U _GLOBAL_OFFSET_TABLE_\n",
+      "                 U __stack_chk_fail\n",
+      "000000000000001e T test()\n",
+      "0000000000000000 t test()::{lambda(int&)#1}::operator()(int&) const\n"
+     ]
+    }
+   ],
+   "source": [
+    "!nm -C lambda.o"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ab79093e",
+   "metadata": {},
+   "source": [
+    "Okay, so that really looks very specific. Indeed **\"the type of a closure cannot be named\"** (at least not explicitely), but it can be detected by **`auto`**."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d65d40b3",
+   "metadata": {},
+   "source": [
+    "***\n",
+    "Another example from *Modernes C++ in der Praxis*. Here we implement a join method for strings."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "03a872a9",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Overwriting join.cpp\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%file join.cpp\n",
+    "\n",
+    "#include <iostream>\n",
+    "#include <algorithm>\n",
+    "#include <string>\n",
+    "#include <vector>\n",
+    "\n",
+    "std::string join( std::vector< std::string >& str, std::string sep = \" \" ) {\n",
+    "\n",
+    "  std::string joinStr = \"\";\n",
+    "\n",
+    "  // small mod from original\n",
+    "  // if( not str.size()() ) return joinStr;\n",
+    "  if( str.size() == 0 ) return joinStr;\n",
+    "\n",
+    "  std::for_each( str.begin(), str.end()-1,\n",
+    "                 [ &joinStr, sep ] (std::string v) { joinStr += v + sep; } );\n",
+    "\n",
+    "  joinStr += str.back(); // back() returns reference to last entry\n",
+    "  return joinStr;\n",
+    "}\n",
+    "\n",
+    "int main() {\n",
+    "\n",
+    "  std::vector< std::string > myVec;\n",
+    "  std::cout << join( myVec ) << std::endl;\n",
+    "\n",
+    "  myVec.push_back( \"One\" );\n",
+    "  std::cout << join( myVec ) << std::endl;\n",
+    "\n",
+    "  myVec.push_back( \"Two\" );\n",
+    "  std::cout << join( myVec ) << std::endl;\n",
+    "\n",
+    "  myVec.push_back( \"Three\" );\n",
+    "  std::cout << join( myVec, \":\" ) << std::endl;\n",
+    "\n",
+    "  myVec.push_back( \"Four\" );\n",
+    "  std::cout << join( myVec, \"/\" ) << std::endl;\n",
+    "\n",
+    "  myVec.push_back( \"Five\" );\n",
+    "  std::cout << join( myVec, \"XXX\" ) << std::endl;\n",
+    "\n",
+    "  std::cout << std::endl;\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "933de670",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!g++ -Wall -Wextra join.cpp"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "id": "c485b43f",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "\n",
+      "One\n",
+      "One Two\n",
+      "One:Two:Three\n",
+      "One/Two/Three/Four\n",
+      "OneXXXTwoXXXThreeXXXFourXXXFive\n",
+      "\n"
+     ]
+    }
+   ],
+   "source": [
+    "!./a.out"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f839546e",
+   "metadata": {},
+   "source": [
+    "Final example from Modernes C++ in der Praxis. Here we implement a flexible filter to operate on a vector of random numbers."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "dcaec377",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Overwriting filter.cpp\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%file filter.cpp\n",
+    "\n",
+    "#include <algorithm>\n",
+    "#include <functional>\n",
+    "#include <iostream>\n",
+    "#include <random>\n",
+    "\n",
+    "// This is going to be our filter\n",
+    "std::function< bool(int) > inRange( int low, int up ) {\n",
+    "  return [low,up] ( int d ) { return d >= low && d <= up; };\n",
+    "}\n",
+    "\n",
+    "\n",
+    "int main() {\n",
+    "\n",
+    "  std::cout << std::boolalpha << std::endl;\n",
+    "\n",
+    "  std::cout << \"4 inRange(5,10): \" << inRange(5,10)(4) << std::endl;\n",
+    "\n",
+    "  auto filt = inRange(5,10);\n",
+    "\n",
+    "  std::cout << \"5 inRange(5,10): \" << filt(5) << std::endl;\n",
+    "\n",
+    "  std::cout << std::endl;\n",
+    "\n",
+    "  const int NUM = 60;\n",
+    "\n",
+    "  std::random_device seed;\n",
+    "  std::mt19937 engine( seed() ); // \n",
+    "\n",
+    "  // distribution\n",
+    "  std::uniform_int_distribution< int > six(1,6);\n",
+    "\n",
+    "  std::vector< int > dice;\n",
+    "  for( int i = 1; i <= NUM; ++i ) dice.push_back( six( engine ) );\n",
+    "\n",
+    "  std::cout << std::count_if( dice.begin(), dice.end(), inRange(6,6) )\n",
+    "    << \" of \" << NUM << \" inRange(6,6)\" << std::endl;\n",
+    "\n",
+    "  std::cout << std::count_if( dice.begin(), dice.end(), inRange(4,6) )\n",
+    "    << \" of \" << NUM << \" inRange(4,6)\" << std::endl;\n",
+    "\n",
+    "  // remove all elements for 1 to 4\n",
+    "  dice.erase( std::remove_if( dice.begin(), dice.end(), inRange(1,4) ),\n",
+    "              dice.end() );\n",
+    "\n",
+    "  // remove_if returns a past-the-end iterator for the new end of the range.\n",
+    "\n",
+    "  std::cout << \"All numbers 5 and 6: \";\n",
+    "  for( auto v: dice ) std::cout << v << \" \";\n",
+    "\n",
+    "  std::cout << \"\\n\\n\";\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "b45b0e4f",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!g++ -Wall -Wextra filter.cpp"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "f85b1bc8",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "\n",
+      "4 inRange(5,10): false\n",
+      "5 inRange(5,10): true\n",
+      "\n",
+      "13 of 60 inRange(6,6)\n",
+      "38 of 60 inRange(4,6)\n",
+      "All numbers 5 and 6: 5 5 6 6 5 6 6 5 5 6 6 5 6 6 5 5 5 6 5 6 6 6 6 5 5 \n",
+      "\n"
+     ]
+    }
+   ],
+   "source": [
+    "!./a.out"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "0ac98da0",
+   "metadata": {},
+   "source": [
+    "***\n",
+    "We had seen that we can store a lambda expression as an object (of some implicit type). However, we can also convert it into a\n",
+    "- function pointer\n",
+    "- std::function"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "cf33e115",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Writing convert.cpp\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%file convert.cpp\n",
+    "\n",
+    "#include <iostream>\n",
+    "#include <functional>\n",
+    "\n",
+    "int main() {\n",
+    "\n",
+    "  using fType = int (*) (int);\n",
+    "\n",
+    "  using sType = std::function< int(int) >;\n",
+    "\n",
+    "  auto impl = [] (int a) { return a+1; };\n",
+    "\n",
+    "  std::cout << \" 1 + 1 = \" << impl( 1 ) << std::endl;\n",
+    "\n",
+    "  fType expl1 = impl;\n",
+    "  std::cout << \" 2 + 1 = \" << expl1( 2 ) << std::endl;\n",
+    "\n",
+    "  sType expl2 = impl;\n",
+    "  std::cout << \" 3 + 1 = \" << expl2( 3 ) << std::endl;\n",
+    "\n",
+    "  std::cout << \"Type of impl is function pointer? \" \n",
+    "            << std::boolalpha\n",
+    "            << std::is_same< fType, decltype( impl ) >::value\n",
+    "            << std::endl;\n",
+    "\n",
+    "  std::cout << \"Type of expl1 is function pointer? \" \n",
+    "            << std::is_same< fType, decltype( expl1 ) >::value\n",
+    "            << std::endl;\n",
+    "\n",
+    "  std::cout << \"Type of expl2 is instance of std::function? \" \n",
+    "            << std::is_same< sType, decltype( expl2 ) >::value\n",
+    "            << std::endl;\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "3fbad575",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!g++ convert.cpp"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "0584e221",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      " 1 + 1 = 2\n",
+      " 2 + 1 = 3\n",
+      " 3 + 1 = 4\n",
+      "Type of impl is function pointer? false\n",
+      "Type of expl1 is function pointer? true\n",
+      "Type of expl2 is instance of std::function? true\n"
+     ]
+    }
+   ],
+   "source": [
+    "!./a.out"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b4fc6130",
+   "metadata": {},
+   "source": [
+    "A closer look at line 12 shows that in the lambda expression, we did not explicitely specify a **return type**. In this case it will automatically be determined from the `return` statement.\n",
+    "\n",
+    "When we want or need to explicitely specify the return type, then this must be done via the *trailing-return-type* syntax."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "id": "ae57e2ba",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "10.000000"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "auto lFunc = [] (double a) -> double { return 2.0*a; };\n",
+    "lFunc( 5.0 )"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e04b7032",
+   "metadata": {},
+   "source": [
+    "***\n",
+    "In HyTeG we often make use of lambdas. Especially for initialising functions and setting boundary conditions. Like in this snippet:   \n",
+    "```c++\n",
+    "real_t k = 2.0;\n",
+    "real_t m = 5.0;\n",
+    "\n",
+    "std::function< real_t(const Point3D&) > solFunc =\n",
+    "  [k, m](const Point3D& x) { sin( k * pi * x[0] ) * sin( m * pi * x[1]; };\n",
+    "\n",
+    "P1Function< real_t > uExact( \"u_analytic\", storage, maxLevel, maxLevel );\n",
+    "uExact.interpolate( solFunc, maxLevel );\n",
+    "```"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "8729d298",
+   "id": "328f2a8e",
    "metadata": {},
    "outputs": [],
    "source": []