Sinais e derivados
Preguiçoso, memorizado, e com as dependências descobertas executando a fórmula.
O derivado é preguiçoso e memorizado#
Ele só recalcula quando alguém lê e alguma dependência mudou. Recalcular na escrita faria uma cadeia de dez derivados rodar dez vezes por mudança — e a maioria deles nunca é lida.
contas := {"n": 0}
action calcular():
contas["n"] := contas["n"] + 1
yield itens.ler()
steady quantos := R.derivado(calcular)
quantos.ler()
quantos.ler()
quantos.ler()
assert contas["n"] is 1 // tres leituras, uma conta soAs dependências são descobertas, não declaradas#
Não há lista para escrever: o derivado roda, e todo sinal lido durante a execução entra. Uma lista escrita à mão envelhece na primeira condição nova dentro da fórmula, e o sintoma é um valor que para de atualizar.
action calcular_frete():
given subtotal.ler() bigger 100.0:
yield 0.0
yield 20.0 - cupom.ler()
steady frete := R.derivado(calcular_frete)Abaixo de 100 o frete leu o cupom, e mexer no cupom o invalida. Acima de 100 a fórmula toma o outro ramo e não lê mais: a partir daí, mexer no cupom não mexe em nada. As fontes que sumiram param de notificar — sem isso, uma fórmula com given acumularia as dependências dos dois ramos e recalcularia por mudanças que ela nem lê mais.
Escrever o mesmo valor não notifica#
Um sinal que avisa sobre x := x faz uma cadeia recalcular por nada e um efeito de rede disparar duas vezes. A comparação pode ser trocada: R.sinal(v, iguais := minha_comparacao).
Ler sem depender#
.valor() devolve o valor sem registrar a dependência — o untracked dos outros frameworks. Sem ele, um derivado que lê um contador de depuração passaria a recalcular a cada incremento dele.
Um ciclo é recusado, com o caminho#
// erro: o derivado 'a' depende de si mesmo.
// nota: a cadeia: a → b → a
// dica: quebre o ciclo: um dos dois precisa ser um sinalDizer apenas "há um ciclo" manda procurar em toda a fórmula; a cadeia é o que o torna quebrável.