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
15Um 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 xCom 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 ** 3Validaçã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 envolvidaQuando 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.