Pular para o conteúdo

Onde dois princípios se contradizem

Nove tensões reais: a escolha, o motivo, o custo aceito e o arquivo onde a decisão mora. Uma lista de princípios diz o que se quer; a tensão diz o que se escolheu.

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 deles venceu — e essa decisão, nesta linguagem, está sempre num arquivo.

dataforge
adopt Arcane.Principios as Prin

// A tensao e o conteudo: a lista diz o que se quer, a tensao diz o que
// se escolheu quando nao era possivel querer as duas coisas.
ts := Prin.tensoes()
assert len(ts) bigger_eq 9
cycle t in ts:
    assert len(t["entre"]) is 2
    assert t["escolha"] is not ""
    assert t["custo"] is not ""        // toda escolha tem preco declarado

out $"{len(ts)} tensoes, e cada uma aponta um arquivo"

Segurança × verificação: o aviso que não virou erro#

Duas threads escrevendo na mesma variável perdem atualizações em silêncio — medido: 40.425 de 80.000. Numa rota do Kiln é pior, porque a concorrência é invisível: seis pedidos simultâneos numa rota que lê, espera e escreve entregaram 1 de 6.

O check avisa. Ele não recusa.

E a lista de métodos que disparam o aviso foi medida, não presumida: append de quatro threads, 5 mil vezes cada, entregou 20.000 de 20.000 — o GIL protege a operação inteira, e avisar sobre ele seria falso alarme em código que funciona.

Verificação × falso alarme: o analisador cala#

A calibragem é 0 erros em 222 arquivos conhecidamente bons. Quando o analisador não consegue provar, ele fica calado — e a lista de silêncios é explícita.

Cala quandoPorque
o outro arquivo não compilaum falso alarme no caminho mais comum de um projeto modular ensina a desligar a verificação
há ciclo de importa leitura não termina, e chutar seria pior
a profundidade (4) acabao custo cresce e a certeza não
o relay nomeia algo que só existe em execuçãonão há como ler o que ainda não rodou
v["k"] ?? padrao?? é exatamente o que a dica daquele erro recomenda — acusar o próprio conserto desligaria a ferramenta
um parâmetro de tipo (T) chega com cara de tiposem isso, a trilha ganhava dois alarmes no capítulo que ensina generics

Velocidade × depurabilidade: o depurador desliga o compilador#

interp.compilar_corpos = False. O depurador para em cada linha sombreando execute, e o corpo compilado passa por fora.

Um depurador que enxerga metade das instruções é pior que um interpretador mais lento: ele mente sobre onde o programa está.

Velocidade × correção: o escopo do laço#

Reaproveitar o escopo entre voltas de um laço é a otimização mais rentável do interpretador. E é a mais perigosa: errá-la não dá erro, dá resposta errada.

dataforge
acoes := []
cycle i from 1 to 3:
    acoes.append(lambda => i)          // cada volta tem o SEU 'i'

assert acoes[0]() is 1
assert acoes[1]() is 2
assert acoes[2]() is 3                 // e nao 3, 3, 3
out "cada closure lembra a volta em que nasceu"

_corpo_captura_escopo varre a árvore inteira, e não só as instruções — um lambda vive dentro de uma expressão. Sem isso, as três closures veriam todas o último valor: o clássico que o Python tem e que aqui não acontece.

Extensibilidade × nomes bons#

route, render, server, agrupar e ordenar são nomes bons demais para tirar de quem escreve. Por isso palavra nova entra como contextual, e não em KEYWORDS.

dataforge
// 'route' e contextual: aqui e um nome comum
route := "/pedidos"
agrupar := yes
assert route is "/pedidos"
assert agrupar is yes
out "as palavras do framework continuam livres"
FamíliaQuantasOnde valem
Kiln11dentro de server e de uma rota
OOP / blueprint21onde um modificador faz sentido
type, opaque, where4quando a linha confirma a declaração
Crucible10numa suíte de teste
verbos de quadro6logo depois de um >>

Runtime modular × zero dependência#

A biblioteca padrão usa apenas a biblioteca padrão do Python. Isso significa código escrito à mão onde havia biblioteca madura:

  • ChaCha20-Poly1305, pelo RFC 8439
  • o .xlsx, sem dependência externa
  • o WebSocket, pelo RFC 6455 — máscara, ping, pong, continuação e quadro de 64 bits
  • os gráficos da Vitrine, em SVG escrito no servidor, com ~4 KB de cliente

O preço está escrito: criptografia e formato de arquivo implementados aqui, com o risco que isso tem. E o ganho também: um app em rede fechada funciona — que é onde painel de dados costuma rodar.

Portabilidade × performance#

A portabilidade é herdada do CPython. É o que dá Linux, macOS, Windows, ARM e ARM64 sem uma linha de código de arquitetura, e o que faz adopt Python.numpy existir.

É também o que põe o teto em ~6,5×. As duas coisas são a mesma decisão vista de dois lados, e não há como ficar só com uma.

Controle × segurança: onde a proteção para#

FFI sem ponteiro não é FFI. Arcane.C recusa o nulo e confere o layout da struct contra a ABI — e aritmética de ponteiro é aritmética de ponteiro.

Mensagem útil × identidade do erro#

O runtime fala português. A tradução acontece no desenho, nunca em error.message.

DecisãoSem ela
traduzir ao desenhare.message é o que um handle compara e o que milhares de testes comparam — traduzir ali mudaria o comportamento de programa já escrito
o que não tem tradução sai em inglêsninguém traduz 530 mensagens numa tacada, e um erro é mais útil legível em inglês que ilegível em português
a suíte roda em inglêsum teste que afirma "Division by zero" checa a estrutura do relatório; deixar o idioma solto o faria reprovar a cada tradução nova
há um piso de coberturao fallback certo é também o que esconde o buraco: o que falta sai em inglês legível, e ninguém vê

O que interessa a um programa é a identidade do erro, não o idioma dele. O que interessa a uma pessoa é o contrário — e as duas coisas caibem, desde que em camadas diferentes.