Pular para o conteúdo

48 · Ecossistema e design

1 exercícios: .

bash
python3 exercicios/run_all.py 48

Os exercícios#

#TítuloEnunciado
266o mapa que nao pode mentir, e o principio que se mede

266 · o mapa que nao pode mentir, e o principio que se mede#

exercicios/48-ecossistema/266_ecossistema_e_principios.df
// As tres ultimas partes da referencia Deep Tech sao as de SINTESE: o
// desenho do ecossistema, os dez principios de design e a visao de
// implementacao. Sao as tres coisas que um projeto costuma escrever
// como prosa — e prosa nao roda, nao reprova e envelhece calada.
//
// Este exercicio mostra as tres feitas de outro jeito: derivadas do
// codigo e CONFERIDAS contra ele.

adopt Arcane.Ecossistema as Eco
adopt Arcane.Principios as Prin
adopt Arcane.Percurso as Perc
adopt Arcane.IO as IO
adopt Arcane.OS as OS

out "== 1. o mapa, conferido nas duas direcoes =="

// A conferencia cobra DUAS coisas, e a segunda e a que importa:
//   faltando  -> caminho citado no mapa que nao existe mais no disco
//   orfaos    -> modulo do nucleo que nao aparece em componente nenhum
//
// Sem a segunda, um modulo novo nasce FORA do mapa e o inventario fica
// incompleto em silencio. Foi ela que pegou o primeiro erro deste
// modulo: o mapa citava 'arcane_concurrent.py', e o arquivo se chama
// 'arcane_paralelo.py' — so a CLASSE se chama ArcaneConcurrent.
c := Eco.conferir()
assert c["ok"] is yes
assert len(c["faltando"]) is 0
assert len(c["orfaos"]) is 0
out $"   {c['citados']} caminhos conferidos, 0 orfaos"

// As tres marcas, e a terceira e a que da valor ao mapa
assert len(Eco.ESTADOS) is 3
assert "nao-existe" in Eco.ESTADOS

n := Eco.numeros()
assert n["existem"] + n["equivalem"] + n["nao_existem"] is n["componentes"]
out $"   {n['componentes']} componentes: {n['existem']} existem, " +
$"{n['equivalem']} equivalem, {n['nao_existem']} nao existem"

out ""
out "== 2. toda ausencia tem motivo escrito =="

// Um mapa que marca tudo como pronto nao e um mapa, e publicidade.
faltam := Eco.o_que_nao_existe()
assert len(faltam) bigger 0
cycle f in faltam:
    assert f["porque"] is not ""
    assert f["aqui"] is not ""

// A maior ausencia, e a razao dela
llvm := [f cycle f in faltam given f["no"] is "LLVM Backend"][0]
assert "fechamentos" in llvm["aqui"]
out $"   {len(faltam)} ausencias nomeadas, cada uma com o porque"

out ""
out "== 3. o principio que se MEDE =="

// A prova nao repete o texto do principio: ela roda o analisador sobre
// quatro trechos com defeito conhecido e conta quantos ele pega ANTES
// de o programa rodar.
p := Prin.conferir("compile-time-first")[0]
assert p["veredito"] is "parcial"
assert "4 de 4" in p["medido"]
out $"   {p['medido']}"

// E o veredito NAO e dez de dez, de proposito: um relatorio que
// aprovasse os dez seria a prova de que ninguem o leu.
v := Prin.veredito()
assert v["total"] is 10
assert v["cumprido"] + v["parcial"] + v["nao-se-aplica"] is 10
assert v["parcial"] bigger 0
out $"   {v['cumprido']} cumpridos, {v['parcial']} parciais, " +
$"{v['nao-se-aplica']} nao se aplica"

out ""
out "== 4. a tensao e o conteudo =="

// Um principio isolado nao informa nada: todo mundo e a favor de
// seguranca e de velocidade. O que informa e ONDE dois se contradizem
// e qual venceu — e essa decisao esta sempre num arquivo.
ts := Prin.tensoes()
assert len(ts) bigger_eq 9
cycle t in ts:
    assert len(t["entre"]) is 2
    assert t["custo"] is not ""  // toda escolha tem preco declarado
    assert t["onde"] is not ""  // e mora num arquivo
out $"   {len(ts)} tensoes, todas com custo e arquivo"

out ""
out "== 5. o percurso, e a fase que NAO roda =="

pasta := $"{OS.temp_dir()}/df-266-{randint(100000, 999999)}"
IO.mkdir(pasta)
alvo := $"{pasta}/exemplo.df"
IO.write(alvo, "action fatorial(n):\n" +
    "    given n smaller_eq 1:\n" +
    "        yield 1\n" +
    "    yield n * fatorial(n - 1)\n" +
    "out fatorial(10)\n")

r := Perc.percorrer(alvo)
assert len(r["fases"]) is 10
assert r["ms"] bigger 0

// A ultima fase e NOMEADA e nao percorrida. Executar e o que o
// programa faz: um arquivo de verdade abre soquete e escreve em disco,
// e um comando que mostra fases nao pode ter efeito no mundo.
ultima := r["fases"][9]
assert ultima["fase"] is "execucao"
assert ultima["percorrida"] is no
assert ultima["ms"] is 0.0

// Apagar a fase do mapa faria o desenho parecer completo. Ela fica.
assert "nao percorrida" in ultima["saiu"]

percorridas := [f cycle f in r["fases"] given f["percorrida"]]
assert len(percorridas) is 9
cycle f in percorridas:
    assert f["ms"] bigger_eq 0.0
    assert f["saiu"] is not ""

// A fase mais cara e uma MEDIDA, e nao se imprime uma medida num
// exercicio: ela muda entre execucoes, e o repositorio compara a saida
// caractere por caractere com a compilacao ligada e desligada. A
// primeira versao imprimia o nome dela e reprovava de forma
// intermitente — 'otimizar' numa execucao, 'tipos' na outra.
//
// O que se AFIRMA e estavel: ela e uma das nove percorridas.
nomes := [f["fase"] cycle f in percorridas]
assert r["mais_cara"] in nomes
out $"   9 fases percorridas, e a mais cara e uma delas"

// E as divergencias em relacao ao desenho do documento
d := Perc.divergencias()
assert len(d) bigger_eq 5
cycle x in d:
    assert x["porque"] is not ""
out $"   {len(d)} divergencias do desenho, com motivo"

IO.remove_tree(pasta)

out ""
out "Tudo verde: o mapa bate com o disco, e os principios foram medidos."

As três últimas partes da referência Deep Tech são as de síntese: o desenho do ecossistema, os dez princípios de design e a visão de implementação.

São as três coisas que um projeto costuma escrever como prosa — e prosa não roda, não reprova e envelhece calada. Este exercício mostra as três feitas de outro jeito: derivadas do código e conferidas contra ele.

1. A conferência nas duas direções#

Arcane.Ecossistema.conferir() cobra duas coisas, e a segunda é a que importa:

DireçãoO que cobraO que impede
faltandotodo caminho citado existe no discoa peça foi renomeada e o mapa aponta para o nome antigo
orfaostodo módulo do núcleo aparece em algum componenteum módulo novo nasce fora do mapa, e o inventário fica incompleto em silêncio

Foi a segunda que pegou o primeiro erro deste módulo. O mapa citava dataforge/stdlib/arcane_concurrent.py em dois componentes. O arquivo se chama arcane_paralelo.py — só a classe se chama ArcaneConcurrent. conferir() acusou os dois antes de o primeiro teste existir.

Sem essa direção, o mapa teria nascido mentindo em dois pontos, e ninguém teria como saber.

2. As três marcas, e por que a terceira é a que vale#

MarcaEstadoQuer dizer
[+]existea peça está aqui, com esse papel
[~]equivaleoutra peça que responde a mesma pergunta, nomeada
[-]nao-existenão há, e o motivo está escrito

A lista é fechada de propósito. Um quarto estado seria o lugar onde "mais ou menos" se esconderia — e é exatamente o que não se quer num inventário.

Sete componentes do desenho estão marcados nao-existe, e o exercício cobra que cada um tenha motivo escrito. Um mapa que marca tudo como pronto não é um mapa, é publicidade: quem o lê descobre a ausência ao tentar, no pior momento, e depois de ter escolhido a linguagem por causa dele.

3. A prova que roda#

Arcane.Principios não repete o texto do princípio. Três das dez provas executam o analisador ou o interpretador:

PrincípioO que a prova fazMediu
compile-time-firstroda o check sobre quatro trechos com defeito conhecido4 de 4 antes de rodar
custo-zerocria um blueprint sem contrato nem invariante e olha o que ele carrega3 de 3 sentinelas em None
seguranca-por-padraoroda o check num thread que escreve de fora e olha a severidadesai como warning

E o veredito não é dez de dez: cinco cumpridos, quatro parciais, um que não se aplica. Um relatório que aprovasse os dez seria a prova de que ninguém o leu.

custo-zero é nao-se-aplica porque a frase do documento — abstrações devem compilar para código equivalente a implementações manuais — não tem como valer sem compilação nativa. Forçá-la a valer seria redefini-la em silêncio. O que vale, e é cobrado, é outra leitura: uma abstração custa zero para quem não a usa.

4. A tensão é o conteúdo#

Um princípio isolado não informa nada. Todo mundo é a favor de segurança, e todo mundo é a favor de velocidade.

O que informa é onde dois princípios se contradizem e qual venceu — e essa decisão, nesta linguagem, está sempre num arquivo. O exercício cobra que cada uma das nove tensões tenha custo declarado e arquivo.

Exemplo: segurança por padrão × verificação antes de rodar. A escrita concorrente é aviso, não erro, porque um acumulador protegido por mutex passa pelo mesmo caminho de um sem proteção — e recusá-lo proibiria o uso correto. Custo aceito, escrito: um programa com bug de concorrência passa pelo check.

5. A fase que não roda#

Arcane.Percurso leva o arquivo por nove fases e mede cada uma. A décima — execucao — é nomeada, medida em zero e marcada como não percorrida.

Executar é o que o programa faz. Um arquivo de verdade abre soquete, escreve em disco e manda e-mail: um comando cuja função é mostrar as fases não pode ter efeito no mundo, e um que tivesse seria usado uma vez.

E ela fica no mapa. Apagá-la faria o desenho parecer completo — a mesma razão por que Machine Code continua no desenho do ecossistema, marcado como ausente.

A armadilha que inverteu a resposta#

A primeira versão do percurso apontava a fase errada, com confiança:

Arquivo de 12 tokensAntesDepois
fase mais caralir, com 93,8%tipos, com 27,2%
total7,266 ms0,526 ms
trabalho real do lir0,05 ms0,064 ms

A causa: lir importa compilador e abre um interpretador por dentro. A primeira fase que toca um módulo paga o import dele, e o cronômetro atribui esse custo a ela.

Uma ferramenta que aponta a fase errada é pior que nenhuma, porque a pessoa vai otimizar o lugar que a ferramenta indicou. Hoje os imports lentos acontecem antes de qualquer cronômetro, e há um comentário ao lado da linha que os aquece — para que ninguém os remova por parecerem inúteis.

O que levar#

  • Um mapa escrito à mão mente sem avisar; um mapa conferido reprova.
  • Nomear a ausência com o motivo vale mais que marcar tudo como

pronto.

  • Um princípio que não se aplica é informação, não um problema a

esconder.

  • A medida precisa ser honesta sobre o que ela é: uma vez, nesta máquina,

para comparar as fases entre si. Para comparar mudanças, há Arcane.Bench.


Rode um isolado com dataforge run exercicios/48-ecossistema/266_ecossistema_e_principios.df.