Carga, ordem e ciclos
Quando o corpo de um módulo executa, o que ele deixa para trás, e por que um ciclo é erro aqui.
Um adopt executa o arquivo, de cima a baixo, uma vez. Tudo o que estiver no topo dele roda — inclusive o que imprime, o que abre arquivo e o que conecta em banco.
O que roda, e quando#
// config.df — tudo isto roda no primeiro 'adopt'
out "lendo a configuração"
steady PORTA := 8000
conexao := abrir_banco() // acontece AQUI, não no primeiro uso
action porta():
yield PORTA
relay portaEfeito colateral no topo de um módulo é a causa mais comum de "por que meu teste abre conexão?". A regra prática é a mesma de qualquer linguagem com carga única: no topo, só declaração e constante; o que custa fica dentro de uma ação.
Ciclo é erro, e não meia resposta#
As outras linguagens escolheram conviver com o ciclo: CommonJS devolve o módulo pela metade, e ESM deixa o nome numa zona morta onde lê-lo é erro de execução. As duas transformam um problema de arquitetura num bug intermitente.
adopt ./y as Y
action daqui():
yield "x"
relay daquiadopt ./x as X
action dali():
yield "y"
relay dalierro[DF0501]: Circular import: x.df → y.df → x.df.
Break the cycle by moving the shared part into a third module.A cadeia inteira aparece. Um ciclo de quatro arquivos é impossível de quebrar sem saber por onde ele passa, e a busca é em largura para achar o ciclo mais curto — que é o mais fácil de romper.
O `check` acha o ciclo antes de rodar#
O ciclo estourava só em execução, no primeiro adopt, e o check passava limpo num projeto que não sobe:
$ dataforge check x.df
x.df:1:1: erro: circular import: x.df → y.df → x.df
sugestão: move the shared part into a third moduleComo quebrar um ciclo#
- Mova o compartilhado para um terceiro módulo. Se
xeyse pedem, quase sempre é porque ambos precisam de um tipo ou de uma constante que não é de nenhum dos dois. - Inverta a dependência. Quem sabe como fazer não deveria conhecer quem manda fazer: passe a ação como argumento em vez de importar o chamador.
- Junte os dois. Dois arquivos que se pedem em círculo frequentemente são um só arquivo que alguém dividiu cedo demais.
Ordem de execução, num programa com módulos#
1. o interpretador lê o arquivo de entrada
2. cada 'adopt' do topo executa o módulo pedido, na ordem em que aparece
3. o módulo, por sua vez, executa os 'adopt' dele — em profundidade
4. um módulo já carregado NÃO executa de novo: devolve o mesmo objeto
5. o corpo do arquivo de entrada roda
6. os 'defer' escritos no topo rodam no fim, inclusive se houve erro