Pular para o conteúdo

Modificadores

private, protected, internal, readonly, override, final, sealed, exclusive, lazy e static steady — o que cada um promete, e quem confere.

Um modificador é uma promessa escrita na declaração. A diferença entre uma promessa e um comentário é que alguém confere: o dataforge check antes de rodar, e o interpretador quando roda. Cada linha da tabela abaixo tem um erro com nome próprio, que um handle pega.

ModificadorVale emPrometeQuebrar dá
privateação, campo, propriedadesó o blueprint que declarou lê, escreve e chamaTypeError
protectedação, campo, propriedadeo blueprint e os herdeirosTypeError
internalação, campo, propriedadesó o arquivo que declarouInternalAccessError
readonlycamposó a construção escreveReadOnlyFieldError
static steadycampo estáticoconstante de classeConstantReassignmentError
overrideação, propriedadesubstitui um membro herdadoOverrideTargetError
finalaçãoa filha não substituiFinalOverrideError
final blueprintblueprintninguém herdaFinalBlueprintError
sealed blueprintblueprintsó herda quem está no mesmo arquivoSealedBlueprintError
exclusiveaçãouma thread por vez no objeto
lazygetcalcula uma vez por objeto

Visibilidade: private, protected, internal#

private e protected falam de linhagem; internal fala de arquivo. É a visibilidade de um módulo: as peças de dentro conversam, e quem adota o arquivo vê só o público.

dataforge
blueprint Conta:
    private saldo := 0.0
    protected taxa := 0.01
    internal action auditar():
        yield $"saldo {self.saldo}"

    action depositar(v):
        self.saldo += v
        yield self.saldo

blueprint ContaPremium extends Conta:
    action custo(v):
        yield v * self.taxa          // protected: a filha lê

c := spawn ContaPremium()
c.depositar(100)
assert c.custo(100) is 1.0
assert c.auditar() is "saldo 100.0"   // internal: mesmo arquivo

monitor:
    out c.saldo
    assert no
handle TypeError as e:
    assert "private" in e.message

readonly: identidade que não muda#

Um campo readonly recebe valor enquanto o objeto nasce — no padrão, no cabeçalho, no setup, no corpo solto do blueprint — e depois disso é recusado. O check também acusa um método comum que o escreve.

dataforge
blueprint Pedido:
    readonly id := 0
    readonly criado_em := "2026-09-17"
    itens := []

    action setup(id):
        self.id := id                 // construindo: permitido

p := spawn Pedido(42)
assert p.id is 42

monitor:
    p.id := 43
    assert no
handle ReadOnlyFieldError:
    out "o id de um pedido não muda"

// 'with' constrói OUTRO objeto, e por isso pode mudar um readonly
copia := p with {"id": 7}
assert copia.id is 7 and p.id is 42

Constante de classe: static steady#

dataforge
blueprint Http:
    static steady PORTA_PADRAO := 80
    static conexoes := 0

Http.conexoes := Http.conexoes + 1     // estático comum: muda
assert Http.PORTA_PADRAO is 80

monitor:
    Http.PORTA_PADRAO := 8080
    assert no
handle ConstantReassignmentError:
    out "constante é constante" 

override: a promessa que pega o erro de digitação#

Sem override, um método com o nome quase certo vira um método novo, e o da mãe continua rodando — em silêncio. Com override, o nome errado é erro, com sugestão.

dataforge
blueprint Animal:
    action falar():
        yield "..."

blueprint Gato extends Animal:
    override action falar():
        yield "miau"

assert (spawn Gato()).falar() is "miau"
text
blueprint Cachorro extends Animal:
    override action fala():     // erro[override-sem-alvo]: did you mean 'falar'?
        yield "au"

final e sealed: fechar a hierarquia#

final blueprint fecha para todo mundo. sealed blueprint fecha para fora do arquivo: a família é conhecida e completa, que é o que deixa um match confiar que cobriu os casos.

dataforge
final blueprint Dinheiro(centavos):
    action somar(outro):
        yield spawn Dinheiro(self.centavos + outro.centavos)

abstract sealed blueprint Forma:
    abstract action area()

blueprint Quadrado(lado) extends Forma:
    action area():
        yield self.lado ** 2

assert (spawn Dinheiro(150)).somar(spawn Dinheiro(50)).centavos is 200
assert (spawn Quadrado(3)).area() is 9

monitor:
    blueprint Moeda extends Dinheiro:
        simbolo := "R$"
    assert no
handle FinalBlueprintError:
    out "use composição: guarde um Dinheiro num campo" 

exclusive: o monitor do objeto#

O check avisa quando duas threads escrevem no mesmo lugar (escrita-concorrente). exclusive é a correção no nível do objeto: só uma thread por vez roda um método exclusivo daquele objeto. A trava é reentrante — um método exclusivo que chama outro não espera por si mesmo.

dataforge
blueprint Estoque:
    quantidade := 0
    exclusive action entrar(n):
        atual := self.quantidade
        self.quantidade := atual + n

e := spawn Estoque()
parallel:
    thread:
        cycle i from 1 to 500:
            e.entrar(1)
    thread:
        cycle i from 1 to 500:
            e.entrar(1)

assert e.quantidade is 1000

lazy: calcular uma vez#

dataforge
blueprint Relatorio(linhas):
    contas := 0
    lazy get total():
        self.contas += 1
        yield sum(self.linhas)

r := spawn Relatorio([10, 20, 30])
assert r.total is 60
assert r.total is 60
assert r.contas is 1        // o corpo rodou uma vez só