Blocos não são objetos.
Depois de muito discutir com um colega sobre os blocos e closures, ele falava que nem tudo em ruby era OO, eu falava que tudo era. Isso era mais confuso que eu esperava.
Bem, em Smalltalk blocos são objetos por si só, em Ruby não, eles são estruturas da linguagem. Mas ai você fala, “se eu passar um bloco por parâmetro e der um .class nele, vai aparecer que ele é um Proc”, de fato, Ruby faz a conversão de bloco para Proc automaticamente, mágica, você ainda pode usar o Proc.new ou o lambda para fazer isso, então seu bloco vira uma closure.
# Percebam o & que admite o método receber um bloco e dentro ser usado como uma closure. def x(&block) puts block.class end
Blocos foram criados inicialmente para loops executarem com yield, mas depois como o próprio Matz explica, foi sendo usado para tudo, então, o que usando são sim objetos.
“…A closure is a nameless function the way it is done in Lisp. You can pass around a nameless function object, the closure, to another method to customize the bhttp://www.nabble.com/Yield-should-be-renamed-call_block-to11494240.html#a11494557ehavior of the method. As another example, if you have a sort method to sort an array or list, you can pass a block to define how to compare the elements. This is not iteration. This is not a loop. But it is using blocks… “
Apesar do Matz falar como se closure fosse diferente de blocos, há quem chame o bloco de closure, como o Martin Fowler:
“I use closures a lot in Ruby, but I don’t tend to create Procs and pass them around.”
Ele não precisa criar os procs, é claro, Ruby faz isso para você!
“…The second difference is less of a defined formal difference, but is just as important, if not more so in practice. Languages that support closures allow you to define them with very little syntax…” - Martin Fowler
Tudo bem, mas ai lendo mais eu chego a isso:
“The each method takes a one argument block (Ruby and Smalltalk both refer to closures as blocks).”
Hum, mas não foi isso que eu entendi lendo o que o Matz falou:
Bill Venners: What makes a block a closure?
Yukihiro Matsumoto: A closure object has code to run, the executable, and state around the code, the scope. So you capture the environment, namely the local variables, in the closure. As a result, you can refer to the local variables inside a closure. Evenhttp://www.martinfowler.com/bliki/Closure.html after the function has returned, and its local scope has been destroyed, the local variables remain in existence as part of the closure object. When no one refers to the closure anymore, it’s garbage collected, and the local variables go away.
Mas ai eu encontro isso que esclarece bastante sobre Procs e blocos:
“Proc objects are blocks of code that have been bound to a set of local variables. Once bound, the code may be called in different contexts and still access those variables.”
- Retirado da documentação do Ruby
Portanto, uma coisa é um bloco (função anônima), outra coisa é uma closure (objeto que contem uma função anônima, um estado e escopo), a conversão no Ruby é automática quando ele é atribuído a alguma variável. Bloco é uma construção sintática realmente interessante, podem não ser objetos, mas isso pouco me importa…
Mais em:
http://www.ddj.com/architect/184404436
http://rubylearning.com/blog/2007/11/30/akitaonrails-on-anatomy-of-ruby-blocksclosures/
http://www.ruby-lang.org/en/about/
http://www.sapphiresteel.com/Ruby-The-Smalltalk-Way-3-The-World
http://www.ruby-forum.com/topic/71221
http://www.neeraj.name/blog/articles/589
http://deadprogrammersociety.blogspot.com/2007/02/ruby-blocks-closures-and-continuations.html
http://www.rubyist.net/~matz/slides/oscon2005/mgp00001.html
http://www.infoq.com/news/2008/01/new-lambda-syntax
http://www.martinfowler.com/bliki/Closure.html
http://www.akitaonrails.com/2007/11/30/anatomia-de-ruby-blocks-closures
http://www.nabble.com/Yield-should-be-renamed-call_block-to11494240.html#a11494557
obs: Estou com muito sono, provavelmente algo tenha passado desapercebido
Será que alguém já pensou/precisou usar um bloco como objeto?! não vejo muito sentido nisso.
Comment by Alisson Sales on October 30, 2008 at 10:12 am
Claro que faz sentido, blocos são convertidos para closures e ai podem ser utilizados como nós tanto fazemos. Bem, é uma questão mais de semântica, no caso, blocos são estruturas sintáticas (podem ser executadas por yields) e closures é quando você da a uma variável o conteúdo de um bloco, então você pode usar ele como qualquer objeto, retornando, chamando mais de uma vez durante o programa etc.
É uma junção do funcional com o OOP que em ruby é bastante interessante!
“Bill Venners: What is the benefit of a real closure? Once I make a block into an object, what can I do with it?
Yukihiro Matsumoto: You can reconvert a closure back into a block, so a closure can be used anywhere a block can be used. Often, closures are used to store the status of a block into an instance variable, because once you convert a block into a closure, it is an object that can by referenced by a variable. And of course closures can be used like they are used in other languages, such as passing around the object to customize behavior of methods. If you want to pass some code to customize a method, you can of course just pass a block. But if you want to pass the same code to more than two methods — this is a very rare case, but if you really want to do that — you can convert the block into a closure, and pass that same closure object to multiple methods”- http://www.artima.com/intv/closuresP.html
Comment by Bastos on October 30, 2008 at 10:52 am
Pode exemplificar com código um caso onde eu precise que um bloco seja um objeto?
Comment by Alisson Sales on October 30, 2008 at 11:06 am
Não é que você “precise”. Ele “tem que ser” para virar uma closure, isso é automático ou não usando o Proc.new, lambda ou proc.
Criando com lambda:
irb(main):001:0> sum = lambda {|a, b| a+b} => #
irb(main):002:0> sum.call(1,1)
=> 2
irb(main):003:0> sum.class
=> Proc
Perceba agora a falta do “&” e usando o sum acima:
Então agora passando um bloco:
irb(main):001:0> def x(a,b,&block) irb(main):002:1> block.call(a, b) irb(main):003:1> end => nil irb(main):004:0> x(1,2) {|n, m| n+m} => 3Usando yield:
irb(main):013:0> def x(a,b,&block) irb(main):014:1> yield a,b irb(main):015:1> end => nil irb(main):016:0> x(1,2) {|a, b| a+b} => 3Comment by Bastos on October 30, 2008 at 11:19 am