James Earl Douglas
November 14, 2019
Alexandre Dumas
┌────────────┐
│ Category C │
├────────────┤
│ X │ <── A collection of objects
│ Y │
│ Z │
│ │
│ X => Y │ <── A collection of arrows
│ Y => Z │
└────────────┘
┌───────────────┐
│ Types │
├───────────────┤
│ Int │ <── Simple types are objects
│ String │
│ │
│ Int => String │ <── Function types are arrows
│ String => Int │
└───────────────┘
┌────────────────────────┐
│ Option[_] │
├────────────────────────┤
│ Option[A] │
│ Option[B] │
│ │
│ Option[A] => Option[B] │
└────────────────────────┘
┌────────────────────────┐ ┌────────────────────┐
│ Option[_] │ │ List[_] │
├────────────────────────┤ ├────────────────────┤
│ Option[A] │ │ List[A] │
│ Option[B] │ │ List[B] │
│ │ │ │
│ Option[A] => Option[B] │ │ List[A] => List[B] │
└────────────────────────┘ └────────────────────┘
HackRF One
GNU Radio Companion
RTL-SDR
Gqrx SDR
BF-F8HP
┌──────────────────┐
│ Flow Graph │
├──────────────────┤
│ Signal │ <── Signals are objects
│ │
│ Signal => Signal │ <── Blocks are arrows
└──────────────────┘
We can use categories to visualize anything we like as long as we can identify the objects and arrows.
┌────────┐
│ C │
├────────┤
│ X │
│ Y │
│ Z │
│ │
│ X => Y │
│ Y => Z │
│ │
│ _ ∘ _ │ <── A composition operator
└────────┘
(Y => Z) ∘ (X => Y) = (X => Z)
n . m
m.andThen(n)
n.compose(m)
{ x => n(m(x)) }
┌───┐ ┌───┐
────────>│ m ├──>│ n ├────────>
└───┘ └───┘
┌───────────────────┐
│ m and then n │
│ │
│ │
────>│ ├────>
│ │
└───────────────────┘
┌───┐ ┌───┐
────────>│ m ├──>│ n ├────────>
└───┘ └───┘
┌───────────────────┐
│ n ∘ m │
│ │
│ ┌───┐ ┌───┐ │
────>│-->│ m │-->│ n │-->├────>
│ └───┘ └───┘ │
└───────────────────┘
Composition hides complexity by abstracting two functions as one.
┌────────┐ ┌───────────────┐
│ C │ │ D │
├────────┤ ├───────────────┤
│ A │ │ │
│ B │ │ │
│ │ │ │
│ A => B │ │ │
└────────┘ └───────────────┘
┌────────────┐
│ Functors │
├────────────┤
│ F: C => D │
└────────────┘
┌────────┐ ┌───────────────┐
│ C │ │ D │
├────────┤ ├───────────────┤
│ A ─────── F.map ─> F[A] │
│ B │ │ │
│ │ │ │
│ A => B │ │ │
└────────┘ └───────────────┘
┌────────────┐
│ Functors │
├────────────┤
│ F: C => D │
└────────────┘
┌────────┐ ┌───────────────┐
│ C │ │ D │
├────────┤ ├───────────────┤
│ A ─────── F.map ─> F[A] │
│ B │ │ │
│ │ │ │
│ A => B ── F.map ─> F[A] => F[B] │
└────────┘ └───────────────┘
┌────────────┐
│ Functors │
├────────────┤
│ F: C => D │
└────────────┘
┌───────────────┐ ┌───────────────────────────────┐
│ Types │ │ Option[_] │
├───────────────┤ ├───────────────────────────────┤
│ Int │ │ │
│ String │ │ Option[String] │
│ │ │ │
│ Int => String │ │ │
└───────────────┘ └───────────────────────────────┘
┌───────────────┐ ┌───────────────────────────────┐
│ Types │ │ Option[_] │
├───────────────┤ ├───────────────────────────────┤
│ Int ──────────── Option[_] ──> Option[Int] │
│ String │ │ Option[String] │
│ │ │ │
│ Int => String │ │ │
└───────────────┘ └───────────────────────────────┘
┌───────────────┐ ┌───────────────────────────────┐
│ Types │ │ Option[_] │
├───────────────┤ ├───────────────────────────────┤
│ Int ──────────── Option[_] ──> Option[Int] │
│ String │ │ Option[String] │
│ │ │ │
│ Int => String ── F.map ──────> Option[Int] => Option[String] │
└───────────────┘ └───────────────────────────────┘
┌───────────────────┐
│ Functors │
├───────────────────┤
│ F: _ => Option[_] │
└───────────────────┘
Time domain signal:
f(t): Time[Signal] = sin(t)
Amplify function:
f(x): Signal => Signal = 0.5 * x
f(t)
│
┌──────────────────┐ ┌──│───────────────────────────┐
│ Signals │ │ │ Time domain signals │
├──────────────────┤ ├──v───────────────────────────┤
│ Signal ──────────── F.map ─> Time[Signal] │
│ Signal => Signal ── F.map ─> Time[Signal] => Time[Signal] │
└──^───────────────┘ └──────────────────────────────┘
│
f(x)
┌───────────────────────────────────┐
│ Functors │
├───────────────────────────────────┤
│ F: Signals -> Time domain signals │
└───────────────────────────────────┘
Functors allow the use objects and arrows from another category.
┌───────┐ ┌───────┐ ┌───────┐
│ D │ │ C │ │ E │
├───────┤ ├───────┤ ├───────┤
│ │ │ │ │ │
│ │ │ │ │ │
│ │ │ X │ │ │
│ │ │ │ │ │
│ │ │ │ │ │
└───────┘ └───────┘ └───────┘
┌───────┐ ┌───────┐ ┌───────┐
│ D │ │ C │ │ E │
├───────┤ ├───────┤ ├───────┤
│ │ │ │ │ │
│ │ │ │ │ │
│ F[X] <─── F.map ─── X │ │ │
│ │ │ │ │ │
│ │ │ │ │ │
└───────┘ └───────┘ └───────┘
┌────────────┐
│ Functors │
├────────────┤
│ F: C => D │
│ │
└────────────┘
┌───────┐ ┌───────┐ ┌───────┐
│ D │ │ C │ │ E │
├───────┤ ├───────┤ ├───────┤
│ │ │ │ │ │
│ │ │ │ │ │
│ F[X] <─── F.map ─── X ─────── G.map ──> G[X] │
│ │ │ │ │ │
│ │ │ │ │ │
└───────┘ └───────┘ └───────┘
┌────────────┐
│ Functors │
├────────────┤
│ F: C => D │
│ G: C => E │
└────────────┘
┌───────┐ ┌───────┐ ┌───────┐
│ D │ │ C │ │ E │
├───────┤ ├───────┤ ├───────┤
│ ┌────────── p ──────────────────────────┐ │
│ │ │ │ │ │ v │
│ F[X] <─── F.map ─── X ─────── G.map ──> G[X] │
│ │ │ │ │ │
│ │ │ │ │ │
└───────┘ └───────┘ └───────┘
┌────────────┐ ┌─────────────────┐
│ Functors │ │ Natural │
├────────────┤ │ transformations │
│ F: C => D │ ├─────────────────┤
│ G: C => E │ │ p: F => G │
└────────────┘ │ │
└─────────────────┘
┌───────┐ ┌───────┐ ┌───────┐
│ D │ │ C │ │ E │
├───────┤ ├───────┤ ├───────┤
│ ┌────────── p ──────────────────────────┐ │
│ │ │ │ │ │ v │
│ F[X] <─── F.map ─── X ─────── G.map ──> G[X] │
│ ^ │ │ │ │ │ │
│ └────────── q ──────────────────────────┘ │
└───────┘ └───────┘ └───────┘
┌────────────┐ ┌─────────────────┐
│ Functors │ │ Natural │
├────────────┤ │ transformations │
│ F: C => D │ ├─────────────────┤
│ G: C => E │ │ p: F => G │
└────────────┘ │ q: G => F │
└─────────────────┘
┌───────────┐ ┌─────────┐
│ Option[_] │ │ List[_] │
├───────────┤ ├─────────┤
│ Option[A] │ │ List[A] │
└─│─────────┘ └─^───────┘
└─────────────────── optionToList ──────────┘
┌───────────┐ ┌───────┐ ┌─────────┐
│ Option[_] │ │ Types │ │ List[_] │
├───────────┤ ├───────┤ ├─────────┤
│ Option[A] │ │ A │ │ List[A] │
└─│─────────┘ └───────┘ └─^───────┘
└─────────────────── optionToList ──────────┘
┌───────────┐ ┌───────┐ ┌─────────┐
│ Option[_] │ │ Types │ │ List[_] │
├───────────┤ ├───────┤ ├─────────┤
│ Option[A] <── O.map ─── A │ │ List[A] │
└─│─────────┘ └───────┘ └─^───────┘
└─────────────────── optionToList ──────────┘
┌────────────────────┐
│ Functors │
├────────────────────┤
│ O: _ => Option[_] │
│ │
└────────────────────┘
┌───────────┐ ┌───────┐ ┌─────────┐
│ Option[_] │ │ Types │ │ List[_] │
├───────────┤ ├───────┤ ├─────────┤
│ Option[A] <── O.map ─── A ─────── L.map ──> List[A] │
└─│─────────┘ └───────┘ └─^───────┘
└─────────────────── optionToList ──────────┘
┌────────────────────┐
│ Functors │
├────────────────────┤
│ O: _ => Option[_] │
│ L: _ => List[_] │
└────────────────────┘
┌───────────┐ ┌───────┐ ┌─────────┐
│ Option[_] │ │ Types │ │ List[_] │
├───────────┤ ├───────┤ ├─────────┤
│ Option[A] <── O.map ─── A ─────── L.map ──> List[A] │
└─│─────────┘ └───────┘ └─^───────┘
└─────────────────── optionToList ──────────┘
┌────────────────────┐ ┌──────────────────────┐
│ Functors │ │ Natural │
├────────────────────┤ │ transformations │
│ O: _ => Option[_] │ ├──────────────────────┤
│ L: _ => List[_] │ │ optionToList: O => L │
└────────────────────┘ └──────────────────────┘
┌──────────────┐ ┌──────────────┐
│ Time │ │ Frequency │
├──────────────┤ ├──────────────┤
│ │ │ │
│ │ │ │
│ Time[Signal] │ │ Freq[Signal] │
│ │ │ │
│ │ │ │
└──────────────┘ └──────────────┘
┌──────────────┐ ┌──────────────┐
│ Time │ │ Frequency │
├──────────────┤ ├──────────────┤
│ ┌────────────── timeToFreq ───┐ │
│ │ │ │ v │
│ Time[Signal] │ │ Freq[Signal] │
│ │ │ │
│ │ │ │
└──────────────┘ └──────────────┘
┌──────────────┐ ┌──────────────┐
│ Time │ │ Frequency │
├──────────────┤ ├──────────────┤
│ ┌────────────── timeToFreq ───┐ │
│ │ │ │ v │
│ Time[Signal] │ │ Freq[Signal] │
│ ^ │ │ │ │
│ └────────────── freqToTime ───┘ │
└──────────────┘ └──────────────┘
Natural transformations convert values between "constructed" types.
Recognizing functional patterns in unfamiliar systems can help us understand them.