Pular para o conteúdo

Planilha reativa

Células que dependem de células — recalculadas sozinhas, só quando alguém lê, e sem o valor que nunca existiu.

Uma planilha é o programa reativo que todo mundo já usou: mudar uma célula atualiza as que dependem dela. O que torna isso difícil é o losango — quando C depende de A e de B, e B também depende de A, uma propagação ingênua mostra por um instante um valor que nunca foi verdade.

PeçaO que ela exercita
R.sinala célula de entrada
R.derivadoa célula com fórmula — preguiçosa e memorizada
R.efeitoquem desenha a tela
a onda de duas faseso losango sem valor intermediário

Estrutura#

text
orcamento/
  src/
    celulas.df     as entradas e as formulas
    tela.df        o efeito que imprime
  tests/
forge.toml
[project]
name = "orcamento"
version = "0.1.0"
description = "Orçamento reativo"
entry = "src/main.df"
dataforge = ">=1.1"

[dependencies]

[scripts]
start = "run src/main.df"
test = "test tests/"

O núcleo#

Este bloco roda sozinho — copie para um arquivo e rode dataforge run. Ele termina com assert, e é assim que esta página é conferida a cada build.

src/celulas.df
adopt Arcane.Reativo as R

steady receita := R.sinal(10000)
steady custo_fixo := R.sinal(4000)
steady margem := R.sinal(0.2)

contas := {"n": 0}
action calcular_variavel():
    contas["n"] += 1
    yield receita.ler() * 0.3

steady custo_variavel := R.derivado(calcular_variavel)
steady lucro := R.derivado(lambda => receita.ler() - custo_fixo.ler() - custo_variavel.ler())
steady meta := R.derivado(lambda => receita.ler() * margem.ler())

vistos := []
steady tela := R.efeito(lambda => vistos.append(lucro.ler()))

assert lucro.ler() is 3000.0
assert contas["n"] is 1

// 'lucro' le 'receita' direto E via 'custo_variavel': o losango.
receita.escrever(20000)
assert lucro.ler() is 10000.0

// O efeito viu 3000 e depois 10000 — nunca o intermediario de 13000
// (20000 - 4000 - o custo velho de 3000).
assert vistos is [3000.0, 10000.0]
assert meta.ler() is 4000.0
out vistos

O teste#

No projeto, a regra mora em src/ e o teste a importa pelo caminho relativo — dataforge test tests/ descobre o arquivo sozinho.

tests/nucleo_test.df
adopt ../src/celulas as C

crucible "planilha":
    trial "ler duas vezes nao recalcula":
        antes := C.contas["n"]
        C.lucro.ler()
        C.lucro.ler()
        expect C.contas["n"] is antes

As decisões#

DecisãoSem ela
o derivado é preguiçosocada leitura recalcula a planilha inteira
propagação em duas fasesa tela pisca um lucro de 13 mil que nunca existiu
o efeito roda uma vez por ondaa tela redesenha duas vezes por mudança
dependências descobertas na execuçãoa lista de dependências escrita à mão envelhece na primeira fórmula nova

Para ir além#

  • A explicação do losango, medida: Arcane.Reativo.
  • Agrupar três escritas numa notificação: R.lote.
  • Fluxos (não valores): R.observavel.

Volte para todos os tipos de projeto.