···47474848| Article | Description |
4949|---------------------------------------------------------|--------------------------------------------------------|
5050+| [Why These Strange Names?](./concepts/00-why-these-strange-names.md) | Demystifying FP terminology |
5051| [Why Errors as Values?](./concepts/01-errors-as-values.md) | The exception problem, railway oriented programming |
5152| [Branded Types In Depth](./concepts/02-branded-types.md) | Phantom types, smart constructors, production patterns |
5253| [Typestate Pattern](./concepts/05-typestate-pattern.md) | State machines in the type system |
+2
docs/guides/concepts/02-branded-types.md
···212212// because it bridges impure JavaScript APIs
213213const Url = (s: string): Option<Url> => {
214214 try {
215215+ // Comma operator: evaluates new URL(s) which throws if invalid,
216216+ // then returns the second expression (the branded value)
215217 return (new URL(s), some(s as Url))
216218 } catch {
217219 return none
···98989999### Workarounds Exist But Are Complex
100100101101-Libraries like fp-ts use encoding tricks (TypeScript's module augmentation) to simulate HKT. purus opts for simplicity: explicit functions per type, consistent patterns.
101101+Libraries like fp-ts use encoding tricks to simulate HKT. Here's a simplified view:
102102+103103+```typescript
104104+// fp-ts style: types register themselves via module augmentation
105105+interface URItoKind<A> {
106106+ Result: Result<A, unknown>
107107+ Option: Option<A>
108108+}
109109+110110+type Kind<URI, A> = URItoKind<A>[URI]
111111+112112+// Now you can write generic code
113113+const map = <F extends keyof URItoKind<unknown>, A, B>(
114114+ F: Functor<F>,
115115+ f: (a: A) => B
116116+) => (fa: Kind<F, A>): Kind<F, B> => F.map(f)(fa)
117117+```
118118+119119+This works but adds complexity: URI strings, module augmentation, and type-level lookups.
120120+121121+**purus opts for simplicity:** explicit functions per type (`mapResult`, `mapOption`, `mapEff`) with consistent patterns. You lose some abstraction but gain immediate readability.
102122103123> **New to FP terminology?** Read [Why These Strange Names?](./00-why-these-strange-names.md) first for plain-English explanations and real-world analogies.
104124···331351```
332352333353The function `f` returns a new wrapped value, and `flatMap` "flattens" the result (avoiding `F<F<B>>`).
354354+355355+> **purus naming:** For Result, the flatMap operation is named `chainResult` to match the type. For Eff, it's `flatMap`. Both implement the same Monad pattern.
334356335357### Why Monad Matters
336358
+12
docs/guides/tutorial/02-your-first-effect.md
···147147148148The function returns a Promise. It's wrapped in a thunk `() =>` so it doesn't start immediately.
149149150150+**Why the thunk matters:**
151151+152152+```typescript
153153+// WITHOUT thunk — fetch starts immediately
154154+const eager = fromPromise(fetch("/api")) // Request already in flight!
155155+156156+// WITH thunk — nothing happens until runPromise
157157+const lazy = fromPromise(() => fetch("/api")) // Deferred
158158+```
159159+160160+The thunk defers execution, which is essential for composability. You can pass `lazy` around, combine it with other effects, add timeout or retry logic—all before any network request happens.
161161+150162### async() - Full control over async behavior
151163152164For advanced cases where you need cleanup or cancellation: