Pular para o conteúdo

Decoradores

mark @nome — envolver uma ação sem alterar seu corpo.

A forma#

dataforge
action com_log(fn):
    action envolvida(x):
        out $"  [log] chamada com {x}"
        yield fn(x)
    yield envolvida

mark @com_log
action triplo(x):
    yield x * 3

out triplo(5)
saída
  [log] chamada com 5
15

Um decorador é uma ação que recebe a ação decorada e devolve a substituta. mark @nome faz a troca.

Empilhar#

dataforge
mark @medir_tempo
mark @com_log
action processar(dados):
    yield transformar(dados)

O mais próximo da ação é aplicado primeiro. Aqui: com_log envolve processar, e medir_tempo envolve o resultado.

Com argumentos#

dataforge
action repetir(vezes):
    action decorador(fn):
        action envolvida(x):
            resultado := void
            cycle _ in range(0, vezes):
                resultado := fn(x)
            yield resultado
        yield envolvida
    yield decorador

mark @repetir(3)
action tentar(x):
    out $"tentando {x}"
    yield x

Com argumentos, o decorador é chamado primeiro com eles, e o resultado recebe a ação. São três níveis de aninhamento — por isso decoradores com argumentos merecem um comentário explicando o que fazem.

Casos úteis#

Cache#

dataforge
action cachear(fn):
    memoria := {}
    action envolvida(x):
        chave := str(x)
        given memoria.has(chave):
            yield memoria[chave]
        valor := fn(x)
        memoria[chave] := valor
        yield valor
    yield envolvida

mark @cachear
action calculo_pesado(n):
    yield n ** 3

Validação#

dataforge
action exige_positivo(fn):
    action envolvida(n):
        guard n bigger 0, $"esperava um numero positivo, veio {n}"
        yield fn(n)
    yield envolvida

mark @exige_positivo
action raiz(n):
    yield sqrt(n)

Cronometrar#

dataforge
adopt Arcane.Time as Time

action medir(fn):
    action envolvida(x):
        crono := Time.stopwatch()
        crono.start()
        resultado := fn(x)
        out $"  levou {round(crono.stop() * 1000, 2)} ms"
        yield resultado
    yield envolvida

Quando não usar#

Um decorador esconde comportamento. Isso é bom para preocupações transversais — log, cache, tempo, autorização — e ruim para lógica de negócio, que fica invisível em quem lê a ação.