diff --git a/Playgrounds/02 - Continuation Passing Style.playground/Pages/10 - Push - Pull Duality.xcplaygroundpage/Contents.swift b/Playgrounds/02 - Continuation Passing Style.playground/Pages/10 - Push - Pull Duality.xcplaygroundpage/Contents.swift
new file mode 100644
index 0000000..2d90498
--- /dev/null
+++ b/Playgrounds/02 - Continuation Passing Style.playground/Pages/10 - Push - Pull Duality.xcplaygroundpage/Contents.swift
@@ -0,0 +1,371 @@
+//: [Previous](@previous)
+
+// Quick review
+// Make and infix version of apply
+precedencegroup ApplicationPrecedence {
+ associativity: left
+ higherThan: AssignmentPrecedence
+}
+precedencegroup CompositionPrecedence {
+ associativity: right
+ higherThan: ApplicationPrecedence
+ lowerThan: MultiplicationPrecedence, AdditionPrecedence
+}
+
+// let's do composition as an operator in infix form
+infix operator >>> : CompositionPrecedence // Application
+infix operator |> : ApplicationPrecedence // Application
+
+// Three functions that compose together
+func doubler(_ value: Int) -> Double { .init(value * 2) }
+func toString(_ value: Double) -> String { "\(value)" }
+func encloseInSpaces(_ value: String) -> String { " \(value) " }
+
+// what does it mean to say that functions "compose"
+encloseInSpaces(toString(doubler(14)))
+
+// Note that functions have type and it is the type that allows them to compose
+type(of: doubler)
+
+// I can compose (Int) -> Double with:
+// (Double) -> String with:
+// (String) -> String
+
+// I can take _any_ function of one variable and make it
+// a computed var on the type of the variable
+// This is possible because computed vars are really functions themselves
+extension Double {
+ // underneath this is a function (Double) -> String
+ var toString: String { "\(self)" }
+}
+extension String {
+ // underneath this is a function (String) -> String
+ var encloseInSpaces: String { " \(self) " }
+}
+
+extension Int {
+ // (Int) -> Double
+ var doubler: Double { .init(self * 2) }
+}
+
+// Importantly, _EVERYTHING_ you think of as a `method` on an object
+// is, in fact, a curried function
+extension Int {
+ // (Int) -> () -> Double
+ func yetAnotherDoubler() -> Double { .init(self * 2) }
+
+ // (Int) -> (Double) -> Double
+ func add(someDouble: Double) -> Double { Double(self) + someDouble }
+
+ // (Int) -> (Int, Double) -> Double
+ func multiply(by anInt: Int, andAdd aDouble: Double) -> Double { Double(self * anInt) + aDouble }
+}
+
+extension Int {
+ static func anotherDoubler(_ anInt: Int) -> Double { .init(anInt * 2) }
+}
+
+type(of: Int.anotherDoubler) // (Int) -> Double
+type(of: Int.yetAnotherDoubler) // (Int) -> () -> Double
+type(of: Int.add)
+type(of: Int.multiply)
+
+func yetAnotherDoubler(_ `self`: Int) -> () -> Double {
+ { .init(`self` * 2) }
+}
+type(of: yetAnotherDoubler) // (Int) -> () -> Double
+
+14.doubler
+14.yetAnotherDoubler()
+
+// NB The compiler lies to us about computed vars, it won't tell us the type like
+// it will functions
+type(of: \Int.doubler)
+
+// These statements are exactly equivalent, however
+doubler(14)
+14.doubler
+
+// Reminder I can _always_ rearrange arguments to a function
+func flip(
+ _ f: @escaping (A, B) -> C
+) -> (B, A) -> C {
+ { b, a in f(a,b) }
+}
+
+func flip(
+ _ f: @escaping (A) -> (B) -> C
+) -> (B) -> (A) -> C {
+ { b in { a in f(a)(b) } }
+}
+
+func flip(
+ _ f: @escaping (A) -> () -> C
+) -> () -> (A) -> C {
+ { { a in f(a)() } }
+}
+
+flip(yetAnotherDoubler)()
+
+// Infix form
+// `+` is a function, just in "infix" form
+14 + 13
+
+// we can compose functions
+public func compose(
+ _ f: @escaping (A) -> B,
+ _ g: @escaping (B) -> C
+) -> (A) -> C {
+ { a in g(f(a)) }
+}
+
+
+// This EXACTLY the same as `compose` just named to be an operation in infix notation.
+public func >>>(
+ _ f: @escaping (A) -> B,
+ _ g: @escaping (B) -> C
+) -> (A) -> C {
+ { a in g(f(a)) }
+}
+
+encloseInSpaces(toString(doubler(14)))
+let composedDoubleString = compose(doubler, compose(toString, encloseInSpaces))
+composedDoubleString(14)
+let doubleString = doubler >>> toString >>> encloseInSpaces
+doubleString(14)
+
+
+// inline the compositions "by hand"
+let toStringExplicit = { innerInt in toString(doubler(innerInt) ) }
+let encloseInSpacesExplicit = { outerInt in encloseInSpaces(toStringExplicit(outerInt) ) }
+// lambda calculus form
+let doubleStringExplicit = { outerInt in encloseInSpaces({ innerInt in toString(doubler(innerInt)) }(outerInt) ) }
+doubleStringExplicit(14)
+
+let value = 14
+doubleString(value)
+doubleStringExplicit(value)
+(doubler >>> toString >>> encloseInSpaces)(value)
+
+// Introduce apply as reversed invocation
+// Note: with @inlinable apply(a, f) == f(a)
+func apply(
+ _ a: A,
+ _ f: (A) -> R
+) -> R {
+ f(a)
+}
+
+ apply(14, doubler)
+ apply(apply(14, doubler), toString)
+apply(apply(apply(14, doubler), toString), encloseInSpaces)
+apply(apply(apply(14, \Int.doubler), \Double.toString), \String.encloseInSpaces)
+apply(apply(apply(14, \.doubler), \.toString), \.encloseInSpaces)
+
+apply(14, doubleString)
+
+// Note that this is _exactly_ the apply function, just in `infix` form
+public func |>(
+ _ a: A,
+ _ f: (A) -> R
+) -> R {
+ f(a)
+}
+
+// Given the above, all of these are EXACTLY the same thing
+// we're just sprinkling on some "syntactic sugar"
+apply(14, doubleString)
+14 |> doubleString
+14 |> \Int.doubler
+14 |> \.doubler
+
+// if we used the infix form of apply, these would be the same as:
+14.doubler
+doubler(14)
+
+// Now lets compare and contrast apply and compose
+// reminder, this is composition
+(doubler >>> toString >>> encloseInSpaces)(14) // Direct Style
+
+// each of these is different, but all yield the same result
+14 |> doubler >>> toString >>> encloseInSpaces // direct style
+14 |> \.doubler >>> \.toString >>> \.encloseInSpaces // direct style
+14 |> doubler |> toString >>> encloseInSpaces // mixed style
+14 |> doubler >>> toString |> encloseInSpaces // mixed style
+14 |> doubler |> toString |> encloseInSpaces // Continuation Passing Style
+14 |> \.doubler |> \.toString |> \.encloseInSpaces // Continuation Passing Style
+14 .doubler .toString .encloseInSpaces // OOP native style
+
+// writing the above out long hand..
+
+// The last two (when inlined)
+apply(apply(apply(14, doubler), toString), encloseInSpaces)
+encloseInSpaces(apply(apply(14, doubler), toString))
+encloseInSpaces(toString(apply(14, doubler)))
+encloseInSpaces(toString(doubler(14)))
+
+// The first one is in lambda calculus form
+//14 |> doubler >>> toString >>> encloseInSpaces // direct style
+compose(doubler, compose(toString, encloseInSpaces))(14)
+({ outerInt in encloseInSpaces({ innerInt in toString(doubler(innerInt)) }(outerInt) ) })(14)
+
+// The compiler can also optimize _THAT_ to:
+encloseInSpaces(toString(doubler(14)))
+
+// Interestingly the compiler _actually_ uses
+// Single Static Assignment (SSA) form in the final output
+// or you can think of it as what the compiler would put out:
+let a = 14 |> doubler // or doubler(14)
+let b = a |> toString // or toString(a)
+let c = b |> encloseInSpaces // or encloseInSpaces(b)
+
+// In summary, all of these _do_ the same thing
+// but _are not_ the same thing
+(doubler >>> toString >>> encloseInSpaces)(14) // Direct Style
+compose(doubler, compose(toString, encloseInSpaces))(14)
+14 |> doubler >>> toString >>> encloseInSpaces // mixed style
+apply(14, compose(doubler, compose(toString, encloseInSpaces)))
+
+14 |> doubler |> toString >>> encloseInSpaces // mixed style
+apply(apply(14, doubler), compose(toString, encloseInSpaces))
+
+14 |> doubler >>> toString |> encloseInSpaces // mixed style
+apply(apply(14, compose(doubler, toString)), encloseInSpaces)
+
+14 |> doubler |> toString |> encloseInSpaces // Continuation Passing Style
+apply(apply(apply(14, doubler), toString), encloseInSpaces)
+
+// Looking closely at that last one we discover that application
+// and the continuation passing style are something that we
+// already knew as the Object-Oriented style
+14 |> doubler |> toString |> encloseInSpaces // Continuation Passing Style
+14 .doubler .toString .encloseInSpaces // Object-Oriented Style
+// [[[14 doubler] toString] encloseInSpaces] // ObjC syntax
+// encloseInSpaces(toString(doubler(14)))
+
+// OO with immutable types is exactly equivalent to CPS
+
+
+// Now, what does it look like if we _curry_
+// our apply function
+// ((A) -> B) -> B This is a Continuation
+func curriedApply(
+ _ a: A
+) -> (@escaping (A) -> R) -> R {
+ { f in f(a) }
+}
+
+ apply( apply( apply(14, doubler), toString), encloseInSpaces)
+curriedApply(curriedApply(curriedApply(14)(doubler))(toString))(encloseInSpaces)
+
+// BTW, it is instructive to curry compose as well, bc we will be coming
+// back to this.
+public func curriedCompose(
+ _ f: @escaping (A) -> B
+) -> (@escaping (B) -> C) -> (A) -> C {
+ { g in { a in g(f(a)) } }
+}
+
+compose (doubler, compose(toString, encloseInSpaces))(14)
+curriedCompose(doubler)(curriedCompose(toString)(encloseInSpaces))(14)
+
+// Again, note that these all give EXACTLY IDENTICAL results
+// And that translating from one form to another ia a completely
+// mechanical process
+ apply( apply( apply(14, doubler), toString), encloseInSpaces)
+curriedApply(curriedApply(curriedApply(14)(doubler))(toString))(encloseInSpaces)
+
+// Note that apply nests left, compose nests right
+ compose(doubler, compose(toString, encloseInSpaces))(14)
+curriedCompose(doubler)(curriedCompose(toString)(encloseInSpaces))(14)
+
+// Also note that we are able to do this to ANY function that is in
+// the standard one-argument form. And that it is only slightly
+// more complicated in the multi-argument form
+// And that we can do this only because we have generics. Generics
+// are absolutely critical to this ability to compose.
+
+
+// Interesting side-note: suppose we _flip_ the arguments to apply
+
+// Flipped form of apply (here called invoke) turns out to be the natively supported invocation form
+
+// reminder this is the apply from above:
+//public func apply(
+// _ a: A,
+// _ f: (A) -> R
+//) -> R {
+// f(a)
+//}
+
+// Flipping that yields:
+public func invoke(
+ _ f: (A) -> R,
+ _ a: A
+) -> R {
+ f(a)
+}
+
+// And then we _curry_ invoke
+
+func curriedInvoke(
+ _ f: @escaping (A) -> R
+) -> (A) -> R {
+ f
+}
+// curried invoke just turns out to be the identity
+// function operating on the supplied funcion. Making it @inlinable allows the
+// compiler to, in fact remove it and just use the
+// native function invocation operation that is built in
+// to the language
+
+
+// The Big Leap
+// Lifting curriedApply from a structural type, i.e. just a function
+// to be a nominal type, i.e. a struct with a function as it's only member.
+
+// Up to this point we've just been playing with Swift's
+// syntax for functions, rearranging things in various ways
+// and showing that different rearrangements produce identical
+// results. NOW we make real use of what generics give us
+
+// Notes:
+// 1. trailing function from curriedApply becomes the let
+// 2. there are TWO inits: the default init + the init that takes the _leading_ value from curriedApply
+// 3. We add a callAsFunction to denote that this is in fact a lifted function
+// 4. the let is exactly the shape of the Haskell Continuation monad right down to the A and the R
+
+public struct Continuation {
+ public let sink: (@escaping (A) -> R) -> R
+ public init(sink: @escaping (@escaping (A) -> R) -> R) {
+ self.sink = sink
+ }
+ public init(_ a: A) {
+ self = .init { downstream in
+ downstream(a)
+ }
+ }
+ public func callAsFunction(_ f: @escaping (A) -> R) -> R {
+ sink(f)
+ }
+}
+
+//// If this is correct, we should be able to
+//// implement curriedApply using the new type
+//// and have it just work..
+func continuationApply(
+ _ a: A
+) -> (@escaping (A) -> R) -> R {
+ Continuation(a).sink // .sink here is exactly of type: (@escaping (A) -> R) -> R
+}
+
+// Proof that apply, curriedApply, continuationApply and Continuation are the exact same thing:
+apply (apply (apply (14, doubler), toString), encloseInSpaces)
+curriedApply (curriedApply (curriedApply (14)(doubler))(toString))(encloseInSpaces)
+continuationApply(continuationApply(continuationApply(14)(doubler))(toString))(encloseInSpaces)
+Continuation (Continuation (Continuation (14)(doubler))(toString))(encloseInSpaces)
+
+// And again, with suitable inlining, the compiler can reduce every one of these to:
+encloseInSpaces(toString(doubler(14)))
+
+//: [Next](@next)
diff --git a/Playgrounds/02 - Continuation Passing Style.playground/Resources/AkkaStreams-1.pdf b/Playgrounds/02 - Continuation Passing Style.playground/Resources/AkkaStreams-1.pdf
new file mode 100644
index 0000000..a152694
Binary files /dev/null and b/Playgrounds/02 - Continuation Passing Style.playground/Resources/AkkaStreams-1.pdf differ
diff --git a/Playgrounds/02 - Continuation Passing Style.playground/Resources/AkkaStreams-2.pdf b/Playgrounds/02 - Continuation Passing Style.playground/Resources/AkkaStreams-2.pdf
new file mode 100644
index 0000000..971f74a
Binary files /dev/null and b/Playgrounds/02 - Continuation Passing Style.playground/Resources/AkkaStreams-2.pdf differ
diff --git a/Playgrounds/02 - Continuation Passing Style.playground/Resources/AkkaStreams-3.pdf b/Playgrounds/02 - Continuation Passing Style.playground/Resources/AkkaStreams-3.pdf
new file mode 100644
index 0000000..5210161
Binary files /dev/null and b/Playgrounds/02 - Continuation Passing Style.playground/Resources/AkkaStreams-3.pdf differ
diff --git a/Playgrounds/02 - Continuation Passing Style.playground/contents.xcplayground b/Playgrounds/02 - Continuation Passing Style.playground/contents.xcplayground
index 7dad8eb..f936c5b 100644
--- a/Playgrounds/02 - Continuation Passing Style.playground/contents.xcplayground
+++ b/Playgrounds/02 - Continuation Passing Style.playground/contents.xcplayground
@@ -10,5 +10,6 @@
+
\ No newline at end of file
diff --git a/Playgrounds/04 - Even More Structured Concurrency.playground/Pages/01 - Cancellable.xcplaygroundpage/Contents.swift b/Playgrounds/04 - Even More Structured Concurrency.playground/Pages/01 - Cancellable.xcplaygroundpage/Contents.swift
index 5275afb..e479413 100644
--- a/Playgrounds/04 - Even More Structured Concurrency.playground/Pages/01 - Cancellable.xcplaygroundpage/Contents.swift
+++ b/Playgrounds/04 - Even More Structured Concurrency.playground/Pages/01 - Cancellable.xcplaygroundpage/Contents.swift
@@ -1,7 +1,100 @@
//: [Previous](@previous)
-import Foundation
+import _Concurrency
-var greeting = "Hello, playground"
+/*:
+ Tasks can leak
+ */
+
+Task {
+ Task {
+ try await Task.sleep(nanoseconds: 4_000_000_000)
+ print("ending inner task 1")
+ }
+ Task {
+ try await Task.sleep(nanoseconds: 5_000_000_000)
+ print("ending inner task 2")
+ }
+ Task {
+ try await Task.sleep(nanoseconds: 3_000_000_000)
+ print("ending inner task 3")
+ }
+ do {
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+ } catch {
+ print("Cancelled the sleep")
+ }
+ print("ending outer task")
+}
+
+/*:
+A better approach
+ */
+public final class Cancellable: Sendable {
+ private let _cancel: @Sendable () -> Void
+ private let _isCancelled: @Sendable () -> Bool
+ private let _value: @Sendable () async throws -> Output
+ private let _result: @Sendable () async -> Result