O que um closure captura

Prévia grátis 3 min de leitura

O que já foi coberto

A aula "Proc e lambda: uma olhada breve", do curso Blocos Ruby (do..end), já cobriu bastante coisa. &block transforma um bloco recebido num objeto Proc, e Proc.new e proc constroem um diretamente. .call, .() e [] são três formas de rodá-lo. lambda e o literal stabby ->() { } constroem uma versão mais rígida. Aquela aula apontou uma diferença: um lambda levanta ArgumentError com o número errado de argumentos, enquanto um Proc comum deixa o argumento que falta virar nil. Nada disso se repete aqui. Este curso usa Proc e lambda do jeito que aquela aula os deixou, para chegar a algo que ela nunca nomeou: a palavra closure, e o que ela significa.

Um método retorna, e um valor não some

def make_greeter(name)
  greeting = "Hello"
  ->(punct) { "#{greeting}, #{name}#{punct}" }
end

g = make_greeter("Ada")
g.call("!")
# "Hello, Ada!"

make_greeter roda, tem duas variáveis locais, greeting e name, monta um lambda e o devolve. Quando g.call("!") roda, make_greeter já terminou. A chamada acabou, e as variáveis locais de uma chamada de método encerrada normalmente deixam de existir. Mesmo assim, g.call("!") alcança greeting e name e produz "Hello, Ada!".

A definição

Um closure é um Proc ou lambda que captura as variáveis locais visíveis no momento em que é criado. Ele mantém acesso vivo a elas enquanto o próprio Proc ou lambda existir, esteja ou não rodando o método que definiu essas variáveis. Todo Proc e todo lambda em Ruby é um closure. É uma propriedade que os dois têm, e não um terceiro tipo de objeto.

Acesso vivo, não uma foto

"Acesso vivo" é uma afirmação que dá para checar, e quer dizer mais que "lembra". Mude uma variável capturada depois de criar o closure, antes de chamá-lo, e veja qual valor ele recebe:

def make_reporter
  status = "pending"
  reporter = -> { status }
  status = "done"
  reporter
end

r = make_reporter
r.call
# "done", não "pending"

status valia "pending" quando reporter foi criado. Se um closure copiasse as variáveis capturadas na criação, r.call devolveria "pending" para sempre. Ele devolve "done", então o lambda guarda uma ligação com a variável status, e não uma cópia congelada do valor que ela tinha quando o lambda foi montado.

Por que isso importa no resto do curso

Todas as aulas seguintes dependem desse fato. Um Proc ou lambda é mais que "um código para rodar depois": é código junto com acesso contínuo às variáveis locais que existiam em volta dele quando foi criado. A próxima aula se apoia nisso. O método dela devolve um pedaço de estado que nenhum outro código alcança, a não ser pelo closure que ele retorna.

Pontos-chave

  • Um closure é um Proc ou lambda que captura as variáveis locais visíveis na criação e mantém acesso vivo a elas depois que o método que as definiu retorna.
  • Todo Proc e todo lambda em Ruby é um closure; é uma propriedade deles, e não um tipo separado de objeto.
  • Mudar uma variável capturada depois de criar o closure, antes de chamá-lo, muda o que o closure vê, então ele guarda uma ligação viva, e não uma foto.
  • O resto do curso se apoia nesse acesso vivo, começando por um método que devolve estado alcançável só pelo closure.
Aula 1 de 7 · grátis

Continue com a aula 2

Uma fábrica de contadores com estado próprio

Monta make_counter, cujo lambda reatribui o próprio count privado, e cada chamada gera estado independente.

Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.

Digite para buscar...