Teu progresso
0 / 83 módulos0%
Estágio 01 · 01-06
BloqueadoUm paradigma é um modo de pensar sobre programa. Cada um tem assumptions diferentes sobre o que é dado, computação, e estado. Linguagens encorajam um paradigma (Haskell = funcional puro, Java = OO clássico) mas a maioria moderna é multi-paradigma (TS, JS, Python, Rust).
Não dominar paradigmas significa que:
O ponto: não escolher um paradigma como religião. Escolher por problema. Pra isso você precisa entender os 3 profundamente.
Princípio: programa é uma sequência de comandos que modificam estado. Modelo do hardware nu.
let total = 0;
for (let i = 0; i < items.length; i++) {
total += items[i].price;
}
if, while, for, goto (raramente hoje).Bom em: algoritmos com loops, manipulação de baixo nível, hot paths onde controle explícito de memória/operações importa.
Ruim em: quando você quer descrever uma transformação sem se preocupar com como (composição funcional resolve melhor), ou modelar entidades com comportamento (OO resolve melhor).
Procedural: subset de imperativo onde código é organizado em procedimentos (funções com efeito), com escopo lexical mas sem objetos. C clássico.
Princípio: programa é coleção de objetos que se comunicam por mensagens (chamadas de método). Cada objeto tem estado (atributos) e comportamento (métodos).
class Order {
constructor(private items: OrderItem[]) {}
total(): number {
return this.items.reduce((sum, i) => sum + i.subtotal(), 0);
}
addItem(item: OrderItem): void {
this.items.push(item);
}
}
4 conceitos canônicos:
private, protected, public.class B extends A, B é A. Reutiliza implementação. Cuidado: herança profunda vira pesadelo. Prefira composição.SOLID (Robert Martin):
SOLID é guia, não dogma. Aplicado mecanicamente vira boilerplate.
Composition over Inheritance: regra cardinal. class Bird extends Animal cria árvore rígida; class Bird { constructor(private fly: FlyBehavior) {} } é flexível.
Design Patterns (Gang of Four):
Conheça os nomes (vocabulário), aplique com parcimônia. Singleton é frequentemente anti-padrão (estado global disfarçado).
Críticas legítimas a OO clássico:
MathHelper.add(1, 2)).Princípio: programa é composição de funções. Funções são first-class (podem ser passadas, retornadas, armazenadas). Estado é evitado ou explicitamente isolado.
Conceitos centrais:
Mesma entrada → mesma saída. Sem efeitos colaterais (não muda variáveis fora, não faz I/O).
// pura
const add = (a: number, b: number) => a + b;
// impura (efeito colateral: muta arr)
function impureSort(arr: number[]) { arr.sort(); }
Vantagens de funções puras:
Dados não mudam. Operações retornam novos dados.
const arr = [1, 2, 3];
const novo = [...arr, 4]; // arr não muda
Vantagens: raciocínio mais simples, undo gratuito, concorrência fácil.
Custo: alocação. Estruturas persistentes (Immutable.js, immer) usam structural sharing pra mitigar.
Funções que recebem ou retornam funções.
const compose = <A, B, C>(f: (b: B) => C, g: (a: A) => B) => (a: A) => f(g(a));
map, filter, reduce são HOFs.
const add = (a: number) => (b: number) => a + b;
const add5 = add(5); // partial
add5(3); // 8
const transform = pipe(
filter(x => x > 0),
map(x => x * 2),
reduce((sum, x) => sum + x, 0),
);
Tipo que encapsula computação com estrutura. Define unit (lift valor) e bind (encadear). Exemplos:
Promise (encapsula computação assíncrona)Optional/Maybe (valor ou nada)Either/Result (sucesso ou erro)Array (zero ou mais valores)Exemplo Maybe:
type Maybe<T> = { kind: 'just'; value: T } | { kind: 'nothing' };
const map = <A, B>(m: Maybe<A>, f: (a: A) => B): Maybe<B> =>
m.kind === 'just' ? { kind: 'just', value: f(m.value) } : m;
const flatMap = <A, B>(m: Maybe<A>, f: (a: A) => Maybe<B>): Maybe<B> =>
m.kind === 'just' ? f(m.value) : m;
flatMap (também chamado bind/>>=) é a operação monádica chave. Permite encadear sem unwrapping/wrapping manual.
Computar só quando necessário. Permite estruturas infinitas (lista de todos primos), curto-circuito, performance via fusão.
function* naturals() {
let i = 0;
while (true) yield i++;
}
// generator é lazy: gera valor por valor, sob demanda
Sum types (tipo A | tipo B) e product types ({ x: A, y: B }). Combinados, modelam domínio com precisão.
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; side: number }
| { kind: 'rectangle'; width: number; height: number };
const area = (s: Shape): number => {
switch (s.kind) {
case 'circle': return Math.PI * s.radius ** 2;
case 'square': return s.side ** 2;
case 'rectangle': return s.width * s.height;
}
// TS conhece exhaustiveness, sem default, erro se case faltar.
};
Pattern matching (em TS via switch + discriminated union; em Rust/Haskell nativamente) é o pão da programação funcional moderna.
Linguagens puras (Haskell, PureScript) proíbem efeitos sem encapsulamento monádico. Linguagens multi-paradigma (JS, TS, Scala, Rust) permitem estilo funcional sem rigor total, escolher quando vale.
Subset/extensão funcional. Programa é fluxo de eventos (Observable). Operadores transformam fluxos.
import { fromEvent } from 'rxjs';
import { map, filter, debounceTime } from 'rxjs/operators';
fromEvent(input, 'input')
.pipe(
map(e => e.target.value),
debounceTime(300),
filter(q => q.length > 2),
)
.subscribe(query => doSearch(query));
Bom pra UI, eventos, streams, jogos. RxJS, RxJava, ReactiveX são bibliotecas de referência. React Hooks tem inspiração reativa.
| Problema | Paradigma natural |
|---|---|
| Loop apertado, otimização baixa | Imperativo |
| Pipeline de transformação de dados | Funcional |
| Modelar entidades com identidade e ciclo de vida | OO (com cuidado) |
| Sistema de eventos | Reativo / FP |
| Concorrência sem locks | FP (imutável) ou actor model |
| Algoritmos clássicos (sort, search) | Imperativo (loops) ou FP (recursão) |
| Domínios complexos (DDD) | OO + FP (entidades OO; pipelines FP) |
| UI declarativa | FP (React funcional) |
Em TypeScript moderno, a regra prática:
First-class: funções podem ser:
Closure: função + ambiente léxico capturado. Em JS:
function counter() {
let count = 0; // capturada
return () => ++count; // closure sobre count
}
const c = counter();
c(); c(); c(); // 1, 2, 3
Closures explicam: módulos com estado privado, factories, callbacks com contexto. Aprofundamento em 01-07.
Pra passar o Portão Conceitual, sem consultar:
Shape.Implementar Result<T, E> (sum type) e uma pipeline de validação composicional em TypeScript.
Result<T, E> (também chamado Either):
type Result<T, E> =
| { ok: true; value: T }
| { ok: false; error: E };
ok<T>(value: T): Result<T, never>err<E>(error: E): Result<never, E>map<T, U, E>(r: Result<T, E>, f: (t: T) => U): Result<U, E>flatMap<T, U, E>(r: Result<T, E>, f: (t: T) => Result<U, E>): Result<U, E>mapErr<T, E, F>(r: Result<T, E>, f: (e: E) => F): Result<T, F>combine<E>(...results: Result<unknown, E>[]): Result<unknown[], E[]>, agrega múltiplos, retorna lista de erros se algum falhou.Order de logística:
type Order = {
customerId: string;
items: Array<{ sku: string; qty: number; price: number }>;
deliveryAddress: { lat: number; lng: number };
deadline: Date;
};
customerId não vazio, formato UUID v4.items não vazio, cada item: qty > 0, price > 0, sku matches ^[A-Z0-9-]+$.deliveryAddress: lat ∈ [-90, 90], lng ∈ [-180, 180].deadline no futuro.Result + composição: sem throw/try/catch no caminho de validação.field, code, message), agregados (não para no primeiro).vitest/jest pra testes).(input) => Result<output, errors[]>, combinadas por combine/flatMap.Result é melhor que throw/try/catch em domínio de validação?never no return de ok/err ajuda inferência.Validation<T, E> applicative (variante do Result que acumula erros automaticamente em sequência, sem precisar de combine explícito).AsyncResult<T, E>.never é o bottom type. Conditional types simulam computação em tipos.Encerramento: após 01-06 você para de escolher paradigma por inércia. Cada decisão de design (classe vs função, mutável vs imutável, herança vs composição) vira uma escolha consciente baseada no problema.
Threshold de Maestria
Acerte todas as 5 pra marcar o módulo como concluído. Sem pressa, sem timer. Tudo fica salvo no teu navegador.
Q1Qual destas NÃO é uma propriedade de uma função pura?
Q2Você modela 'pássaro' como class Bird extends Animal e depois precisa adicionar Penguin (que é Animal mas não voa) e Bat (que voa mas é Mammal). Qual é o problema e a solução idiomática?
Q3Qual é o uso REAL e idiomático de closure neste código? function counter() { let count = 0; return { inc: () => ++count, get: () => count }; }
Q4Em TypeScript, o tipo type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; side: number } usa qual conceito de paradigmas funcionais e qual sua vantagem prática?
Q5Por que Promise pode ser considerada uma mônada e o que isso te diz sobre como compor operações async?
Destrava
01-06 é prereq dos seguintes módulos: