Lesson 19 / 25
Functors: map as a Pattern
Apply a function inside a container.
One idea, many containers
A functor is a container or context with a map operation that applies a function to the value(s) inside and returns the same kind of container. Arrays map over every element; an Option maps over the value if present and leaves None alone; a Result maps the success value and passes errors through. Lawful functors obey two laws: mapping the identity function changes nothing, and mapping f then g equals mapping their composition. A Promise's then behaves like map when the callback returns a plain value, but it is only an analogy: promises run eagerly and automatically flatten nested promises, so they do not satisfy the laws strictly.
Patterns behind map and chaining
Functors and monads name patterns you already use, and the functional core / imperative shell architecture applies them at system scale.
map for Option
The function runs only when a value exists.
type Option<T> = { tag: 'some'; value: T } | { tag: 'none' };
const some = <T>(value: T): Option<T> => ({ tag: 'some', value });
const none: Option<never> = { tag: 'none' };
const mapOption = <A, B>(o: Option<A>, f: (a: A) => B): Option<B> =>
o.tag === 'some' ? some(f(o.value)) : none;
const len = (s: string) => s.length;
mapOption(some('hello'), len); // { tag: 'some', value: 5 }
mapOption(none, len); // { tag: 'none' }; len is never called
[1, 2, 3].map(n => n * 2); // arrays: same idea over many valuesGift wrapping
map lets you change the gift without unwrapping it yourself: the box (array, Option, Result) handles opening and rewrapping, and an empty box stays empty.
Quick check: Why is Promise only an analogy for a functor?
- Promises are synchronous
- Promises have no then method
- Promises cannot hold values
- It runs eagerly and auto-flattens nested promises, so it does not follow the laws strictly
Answer
It runs eagerly and auto-flattens nested promises, so it does not follow the laws strictly — then mixes map and flatMap behaviour.