Concorrência
A redefinição: shared-nothing, com atores isolados conversando por channels tipados, sob supervisão. O mesmo
Channel[T]serve local e rede.
O conceito
Seção intitulada “O conceito”Concorrência mainstream é threads + memória compartilhada + locks. Você cria threads, elas compartilham o heap, e mutexes/atomics protegem o que é compartilhado. Funciona, mas a categoria de bug “esqueci de sincronizar” é endêmica, e o modelo não atravessa a rede (mutex não funciona entre máquinas).
Como Makoto redefine
Seção intitulada “Como Makoto redefine”A herança é a da BEAM/Erlang: a unidade é o processo, isolado, com heap próprio, barato. Processos
não compartilham memória (não existe @shared). Eles conversam por channel tipado,
Channel[<-T, ->U], onde a notação é a perspectiva de quem segura o handle: <-T = “eu envio T”,
->T = “eu recebo T” (elimina a inversão de tipos na assinatura do outro lado). Operadores: chan <- data (send), data := -> chan (receive), e <-> (request-reply síncrono).
E a virada que unifica dois mundos: o channel de rede é o mesmo Channel[T] do local. Não há
“channel local” vs “RPC remoto”; há um tipo, dois contextos. A física da rede só se expressa por duas
exigências que já existem na linguagem: o payload precisa virar bytes (T + Serializable) e
estabelecer pode falhar (Result + timeout). Isso é o RPC tipado nativo: os dois lados
compartilham T em compilação, sem IDL nem stub gerado.
O raciocínio
Seção intitulada “O raciocínio”Por que shared-nothing? Porque é o que torna o resto do design possível. Sem aliasing concorrente,
o borrow checker fica desnecessário (cap. 02); a memória pode ser colorless; um panic num processo
não corrompe o heap de outro. O isolamento não é uma restrição imposta; é a fundação de onde a
segurança de memória sem lifetimes e a tolerância a falhas brotam.
Por que o <-> exige timeout? Porque dois processos fazendo <-> um no outro dariam deadlock
garantido, e um deadlock silencioso é a pior falha possível: o sistema trava esperando uma resposta
que nunca vem, sem aviso. O prazo obrigatório é a saída estrutural: torna o timing visível e força a
pergunta honesta “e se ele não responder?”. (O <- assíncrono não precisa, porque não espera.)
Por que supervisão emerge da estrutura? A BEAM tem link/monitor/trap_exit como primitivas
cruas. Makoto não as expõe: a morte de um processo é um error comum (rodam os defers, libera-se o
@mm, produz-se o motivo), e observar a morte sem morrer junto é literalmente o que o catch |e| de
um spawn supervisionado já faz; um monitor não é construto novo, é o nome disso. Acoplar destino
(links) é a árvore: processos no mesmo spawn-block vivem e morrem juntos porque a estrutura diz
isso. As três estratégias (one_for_one, one_for_all, rest_for_one) caem da forma do código, não
de um behaviour declarado à parte.
Por que não as alternativas
Seção intitulada “Por que não as alternativas”- Threads + mutex + memória compartilhada (Rust/C++/Java). Reintroduz o aliasing concorrente que obrigaria o borrow checker, espalha a categoria “esqueci o lock”, e não escala para distribuído (mutex não atravessa a rede). Shared-nothing elimina a categoria de bug inteira.
- O modelo Go puro (goroutines +
sync). Go tem channels, mas também permite memória compartilhada e locks, e a porta do aliasing fica aberta. Makoto fecha: só channels, zero shared memory. link()/monitor()crus (Erlang). Cascata de morte por links soltos é não-local: “quem matou quem?” fica implícito. A árvore de supervisão torna o destino acoplado visível na estrutura.- Um tipo de channel separado para rede / um framework de RPC. Reabriria a assimetria “local vs
remoto”. Reusar
Channel[T]comT + Serializabledeixa a física (serialização, falha) se expressar pela constraint e peloResult, sem um segundo modelo de comunicação.
A dor concreta
Seção intitulada “A dor concreta”O data race que só aparece em produção sob carga, porque alguém esqueceu um lock. O sistema que
trava esperando uma resposta de rede que nunca chega, sem timeout, sem log. A cascata de mortes em
Erlang onde um link() esquecido derruba metade do sistema e ninguém sabe o caminho. Cada uma é uma
falha que o isolamento + timeout-obrigatório + árvore-de-supervisão tornam impossível ou visível.
O modelo mental
Seção intitulada “O modelo mental”Processos isolados conversando por channel, supervisionados. A morte é um valor que sobe ao supervisor; ele reinicia, derruba o grupo, ou escala, tudo emergindo da árvore do código.
A fricção aceita
Seção intitulada “A fricção aceita”Shared-nothing custa cópia/passagem de mensagem entre processos: você paga em throughput o que ganha
em correção e em distribuição-nativa. A árvore de supervisão é mais rígida que link() cru: você só
monitora os filhos que você spawnou; observação cross-tree vai por channel normal. Menos flexível
que Erlang, mais estruturado de propósito.
Próximo: 05 · Erros