Monkeypatching is Destroying Ruby

72 respostas
P

[…] To the point that smart, experienced hackers reach for a monkey patch as their tool of first resort, even when a simpler, more traditional solution is possible.

I don?t believe this situation to be sustainable. Where I work, we are already seeing subtle, difficult-to-debug problems crop up as the result of monkey patching in plugins. Patches interact in unpredictable, combinatoric ways. And by their nature, bugs caused by monkey patches are more difficult to track down than those introduced by more traditional classes and methods. As just one example: on one project, it was a known caveat that we could not rely on class inheritable attributes as provided by ActiveSupport. No one knew why. Every Model we wrote had to use awkward workarounds. Eventually we tracked it down in a plugin that generated admin consoles. It was overwriting Class.inherited(). It took us months to find this out.

Eu acho que monkey patching é algo abusado em Ruby. O problema é que o esquema de bindings em Ruby é muito ruim, você não tem como criar blocos de escopo arbitrários ou fazer como em Scala e limitar o escopo de um patch. Eu já tive problemas sérios com 1.minute e conflito entre patches.

72 Respostas

F

O pessoal do zope/plone é um ótimo exemplo de onde monkey patching pode levar. Na minha opnião utilizar isso é prática ruim e detona não só ruby mas qualque linguagem que suporte isso.

C

Sei la, eu acho a alternativa bem pior.

M

Mas qual seria a alternativa?

P

Em Ruby não dá para fazer muito. Você bsicamente pode usar monkey patching para alterar as classes core ou fazer algo no estilo Java onde você tem uma gerência de dependências estáticas, onde um inteiro não pode ser independente de Date em compile time e integrado em runtime.

O problema é que as reras de escopo em Ruby são arcaicas considerando as capacidades de metaprogramação da linguagem. Tudo é muito global e muito irreversível.

M

Algo como cflow e cflowbelow do AspectJ já me deixariam feliz :slight_smile:

P

Scala tem uma ótima saída para isso, com conversões implícitas, e Lisp tem bloco léxicos hierárquicos com LET há décadas. Acho que simplesmente ningu’m pensou que ia precisar disso e como consequência você tem monkey patching e namespace wars.

L

Ruby tem duas coisas que melam a vida de quem quer fazer metaprogramação mais hardcode. Não existe como sobrescrever o mecanismo de resolução de nomes e tão pouco como definir escopos no qual alterações a classes acontecem.

M

A/C

Louds (Moderador GUJ)

Por gentileza, conforme sua explicação, postei a URL abaixo, porque não compreendi tais afirmações sobre Ruby metaprogramação, o que você quer dizer sobre mais hardcode, [b]mecanismo de resolução de nomes , etc …

:idea: Poderia finalizar colocando melhor transparência ao assunto, sitando melhores detalhes sobre tal concepção.[/b]

:arrow: http://www.guj.com.br/posts/list/83661.java

Att.
Marcio Duran

P

Simples, crocodilo, como você faz e quiser definir um método seu, que você criou e faz algo que só você precisa, chamado “times” em Fixnum se você usa Rails?

F

intance_eval tem resolvido bem a minha vida:

def ei_vc(&bloco)
  MeuEscopo.new.instance_eval(&bloco)
end

ei_vc do
  qualquer
  coisa
end

class MeuEscopo
  def qualquer
    # ...
  end
  def coisa
    # ...
  end
  # ...
end
T

A/C
Louds (Moderador GUJ)
Por gentileza, conforme sua explicação, postei a URL abaixo, porque não compreendi tais afirmações sobre Ruby metaprogramação,

O Louds está falando em um nível que é umas duas ordens de grandeza maiores que você pode compreender. Se quiser entender melhor o que ele fala, por favor, sr. Duran, aprenda outras linguagens de programação e um bocadinho de matemática, para que você não reduza Ruby a uma mera “linguagem orientada a objetos”, com você postou aqui no GUJ :stuck_out_tongue:

E leia um pouco mais - sitar é um instrumento musical de origem árabe; acredito que você queria dizer “citar”.

L

[jaba mode on]
Meu comentário sobre isso ficou muito grande e acabei colocando ele no meu blog
http://www.kumpera.net/blog/index.php/2008/03/03/monkeypatching-gambiarra-du-jour/
[jaba mode off]

P

intance_eval tem resolvido bem a minha vida:

Aind anão adianta. Em outro tópico eu citei uma DSL de testes usando RSpec e Selenium, nessa DSL tivemos que azer os objetos de domínio incluírem módulos do RSpec para que pudéssemos usar pending e outros métodos dentro dos nossos blocos que não são closures.

Ruby precisava ter bindings hierárquicos. Hoje em Ruby você so consegue avaliar algo em dois níveis, o primeiro (como intance_eval) e o Object/Kernel.

F

e vc não consegue fazer isso decorando os caras só no escopo do bloco?

algo como o jmock faz em java:

{
  one(obj).pending()...
}

Tudo bem, pode dar uma trabalheira, mas acho que funciona.

P

Desculpa, Fábio, boiei. Decorando o que?

L

intance_eval tem resolvido bem a minha vida:

Aind anão adianta. Em outro tópico eu citei uma DSL de testes usando RSpec e Selenium, nessa DSL tivemos que azer os objetos de domínio incluírem módulos do RSpec para que pudéssemos usar pending e outros métodos dentro dos nossos blocos que não são closures.

Ruby precisava ter bindings hierárquicos. Hoje em Ruby você so consegue avaliar algo em dois níveis, o primeiro (como intance_eval) e o Object/Kernel.

Não precisa ter binding hierárquico, basta ter binding extensível.

code = proc {|a| print a.d }

context_eval(proc) do |obj, name| 
 return "d é mais legal" if name == :d
 obj.send (name)
end

Com isso é trivial implementar instance_eval module_eval e por ai vai. Para completar permita configurar um bloco padrão de resolução por Modulo/classe/método e adicione a possibilidade de andar na pilha e recuperar um bloco que resolva nomes de acordo com o método em questão. Com essas três coisas basicamente tudo que reclamam sobre meta-programação em Ruby tem solução simples.

P

Exemplo do problema:

class A

def bazooka
 @a = "BOOOOM"
 B.new.fire {puts @a}
end
end

class B

def fire(&block)
 instance_eval &block
end

end

A.new.bazooka #boom?
P

Eu diria que não precisava ter binding extensível, bastava ter binding hierárquico que é mais limitado, provavelmente mais fácil de implementar e ainda assim eficiente na maioria dos casos.

F

Eu acho que eu to boiando. Pensei numas coisas doidas aqui, mas só consigo explicar direito vendo o que vc quer fazer.

Procurei o seu tópico da selenium dsl (e pending) e não achei (link?). Posta um snippet?

P

Aqui:

http://www.guj.com.br/posts/list/45/83613

O método 'On <pagina> do {stuff}'faz um instance_eval. azendo isso ele perde o contexto do bloco describe do RSpec, perdendo então o acesso ao m’todo pending().Como pending e stateless a coisa foi resolvida fazendo o cara que implementa o método on() incluir o módulo que declara pending. Uma bela gambiarra :stuck_out_tongue:

F

louds, vc tá cheio de fans:

http://forum.rubyonbr.org/forums/1/topics/2783
http://forum.rubyonbr.org/forums/1/topics/2766

P

Oi, gente,

Eu tô discutindo lá no outro fórum e esse menino falou isso. Será que vocês podem me audar a responder ele? Sabe, ele fica me sacaneando e eu quero dar o troco…

F

pcalcado:
Aqui:

http://www.guj.com.br/posts/list/45/83613.java#446292

O método 'On <pagina> do {stuff}'faz um instance_eval. azendo isso ele perde o contexto do bloco describe do RSpec, perdendo então o acesso ao m’todo pending().Como pending e stateless a coisa foi resolvida fazendo o cara que implementa o método on() incluir o módulo que declara pending. Uma bela gambiarra :stuck_out_tongue:

Tá, agora entendi. Que tal algo do tipo:

module SeleniumOperations
  def search_for() ... end
  def filter_by() ... end
end

def on(pagina, &stuff)
  extend SeleniumOperations
  instance_eval(&stuff)
end

Isso não cria o escopo novo e acho que resolveria o seu problema. Só acho que não ataca o problema maior que eu ainda não consegui capturar de você! :wink:

P

Soluciona o problema porque você etá usando funções apenas, não objetos. Que tal:

class A
 def initialize
  @abc =122
 end

 def hehe
  B.new.huhuhu {p @abc; p bwawawawawa}
 end
end

class B
 def huhuhu(&block)
  instance_eval &block
 end

 def bwawawawawa
  'risada maquiavelica'
 end
end

A.new.hehehe

Eu quero que meu bloco tenha acesso ao escopo de B para poder usar o metodo bwawawawawa() mas ao mesmo tempo tenha acesso ao escopo original, para poder acessar @abc (ou um étodo que retorne abc, tanto faz).

Se não fosse por A ter estado eu poderia incluir B em A, o que seria gambiarra mas funcionaria e é o que fizemos com selenium. Este tipo de situação surge o tempo todo com internal DSLs.

M

Presta atenção nas respostas e perguntas aqui sendo feitas, e tenta aprender alguma coisa que não seja uma cegueira de falta do seu conhecimento, que já vem se passando despercebido.Outra coisa use mensagens privadas para falar sobre mim, isso é mais apropriado, é por isso que o GUJ tem esse recurso.Aqui o assunto é Ruby.
Outra coisa as pessoas sabem se representar, não precisam de segunda voz.
"Em relação a citar e sitar, pra um ignorante como você tudo passa ser uma coisa só "

M

Ainda bem que a “falta de conhecimento” do thingol “já vem se passando despercebido”, porque se alguém percebesse que ele não tem conhecimento em alguma coisa, de certo é algo que estava escondido nos confins do universo :stuck_out_tongue:

M

Maurício Linhares:

confins do universo :P

Você bem que podia ficar lá, o assunto aqui é Ruby.

T

Ué, onde está essa mensagem que o Marcio Duran se referiu?

M

“Acho não caiu a ficha em você”

D

Márcio, por favor, não desvie o assunto do tópico. Se você tiver alguma coisa a acrescentar de útil a este tópico, por favor, faça-o. Caso contrário, evite postar mensagens fora do assunto central do tópico. Como você mesmo disse, existem mensagens privadas para isso. Assunto encerrado.

M

Peço a genteliza de passar essa mensagem aos demais que estão fazendo-o tamanha bossalidade e simplesmente vem de faixada aqui, e que não estão explorando o assunto.

Como disse questionei, o Louds por suas afirmações todavia as mesmas estão sendo questionadas aqui, não tomo partido por ninguem e eu espero que você faça o mesmo.

Att.
Marcio Duran

N

Voltando ao tópico.

Marcio Duran, o que você não entendeu mesmo ?
Tem tantas referencias ali que já não sei mais.

Sobre metaprogramação , resolução de nomes ?

M

nbluis:
Voltando ao tópico.

Marcio Duran, o que você não entendeu mesmo ?
Tem tantas referencias ali que já não sei mais.

Sobre metaprogramação , resolução de nomes ?

Veja a afirmação do Louds sobre Escopo…, não entendi…

L

pcalcado:

Eu quero que meu bloco tenha acesso ao escopo de B para poder usar o metodo bwawawawawa() mas ao mesmo tempo tenha acesso ao escopo original, para poder acessar @abc (ou um étodo que retorne abc, tanto faz).

Se não fosse por A ter estado eu poderia incluir B em A, o que seria gambiarra mas funcionaria e é o que fizemos com selenium. Este tipo de situação surge o tempo todo com DSLs.

Uma dúvida, se você casar um par de instance eval/ module_eval para instancias de definem apenas os *_missing não é possivel capturar todos bindings?

N

Marcio Duran:

Veja a afirmação do Louds sobre Escopo…, não entendi…

Isso é exatamente o que está sendo discutido no tópico.
Este tópico tem um assunto mais complexo do que se está acostumado por ai.

Falam sobre o escopo de blocos dentro de ruby. Veja os exemplos do shoes que estão bem esplicados.

E inclusive a impossibilidade de limitar escopo de alterações feitas no core da linguagem possibilitada pelas classes abertas.

O post do louds sobre monkeypatching ficou bem legal.
Dá uma lida alí.

F

pcalcado:
Soluciona o problema porque você etá usando funções apenas, não objetos. Que tal:

class A
 def initialize
  @abc =122
 end

 def hehe
  B.new.huhuhu {p @abc; p bwawawawawa}
 end
end

class B
 def huhuhu(&block)
  instance_eval &block
 end

 def bwawawawawa
  'risada maquiavelica'
 end
end

A.new.hehehe

Eu quero que meu bloco tenha acesso ao escopo de B para poder usar o metodo bwawawawawa() mas ao mesmo tempo tenha acesso ao escopo original, para poder acessar @abc (ou um étodo que retorne abc, tanto faz).

Se não fosse por A ter estado eu poderia incluir B em A, o que seria gambiarra mas funcionaria e é o que fizemos com selenium. Este tipo de situação surge o tempo todo com DSLs.

Emulemos então o binding hierárquico!

class A
  def pele
    "aaabbbbccc"
  end

  def hehe
    B.new.huhuhu(self) {p pele; p bwawawawawa}
  end
end  

class B  
  def huhuhu(parent_context, &block)
    (class &lt;&lt; self; self; end).send(:define_method, :method_missing) do |method, *args|
      parent_context.send(method, *args)
    end
    instance_eval &block
  end  

  def bwawawawawa  
    'risada maquiavelica'  
  end  
end  

A.new.hehe

Agoro Ruby, e adoro metaprogramação! :twisted:

L

Você tá simulando herança aqui, não binding hierárquico.

N

louds:

Você tá simulando herança aqui, não binding hierárquico.

Isso é bastante usado em javascript para fornecer herança.

Prototype é um exemplo disso.

T

Bom, vou voltar aqui que a poeira abaixou, e vou fazer minhas declarações:

  • Eu não sei Ruby;
  • Entretanto, eu sei o que é metaprogramação e problemas de scoping e outros quetais;
  • E vou fazer minha digressão aqui.

Há dois tipos de programadores: os “blue-collars” e os “white-collars”. *
O sr. Gosling tinha afirmado uma vez que Java era para os programadores “blue-collar”, ou seja, aqueles caras que antigamente se matavam para fazer os programas em C++ funcionarem, e agora se matam para fazer os programadores Java funcionarem no mundo real - deploys, web services e outras chateações. Eles não têm muitas idéias de conceitos abstratos, e é uma vitória que eles finalmente consigam entender corretamente o conceito de orientação a objeto, que levou muitas décadas para ser aceito.
Os “white-collars” são aqueles que entendem todos esses conceitos e criam novos, ou pelo menos têm a coragem de irem além desses conceitos. Programação funcional ou genérica para eles é o seu café da manhã, e é coisa muito antiga para eles - afinal de contas, quantos 50 anos tem a linguagem Lisp? Normalmente são os que falam de assuntos “esotéricos”.

O sr. Marcio Duran (um clássico “blue-collar” - eles é que fazem o mundo girar) entrou no meio de uma discussão de “white-collars” e esperou tentar entender alguma coisa rapidamente**, só que não percebeu que “entrou na sala errada” e exigiu ser tratado como um igual. Aí é que ocorreu o problema.


  • Não estou usando aqui “white-collar” no sentido em que se dá nas editorias de política, que tratam de muitos “crimes de colarinho branco”. Nada disso :stuck_out_tongue: - uso em contraposição a “blue-collar”, que é o colarinho de um macacão azul de um operário de fábrica.
    ** Como bom “blue-collar”, ele não tem tempo de tentar entender essas coisas “esotéricas” e acha tudo uma grande besteira.
F

Tem razão, mas nesse caso o efeito é o mesmo, certo?

E de qualquer forma, prefiro mesmo “simular herança” com delegação do que usar herança de verdade. :twisted:

P

É basicamente o que venho utilizando hoje para contornar. O problema é que preciso avaliar em elo menos 3 bindings: do closure, da classe que recebe o closure e top-level. Conseguir o primeiro é complicado, a menos qu você faça a gambi que o Kung posto de passar self no método :stuck_out_tongue:

F

gambi essa minha que é melhor que a gambi de incluir o modulo! :smiley:

M

Tem algum jeito de pegar o call stack atual em Ruby como tem em Java?

P

Estamos discutindo qual gamiarra é melhor

CQD :smiley:

F

Tem isso, mas não serve:
http://ruby-doc.org/core/classes/Kernel.html#M005955

Do jeito que você quer não conheço…

M

:thumbup: O assunto continua sendo Ruby, mas posso dizer que você tem tamanha imaginação para canditado a “blue-collar”.

:idea: “Em relação a querer aprender ou ter conhecimento isso vai da vontade e busca da investigação ao saber de cada um”

“Apostila Ruby on Rails fique avontade para abrir sua mente”

Ruby on Rails

http://linorg.ciagri.usp.br/ftp/pub/webdev/rails-tutorial.pdf

L

Tem isso, mas não serve:
http://ruby-doc.org/core/classes/Kernel.html#M005955

Do jeito que você quer não conheço…

O povo do rubinius tava fazendo algo no sentido de permitir stack walk arbitrário, sendo possível Não sei se é possível capturar com contexto de um frame, entretanto.

M

Tem isso, mas não serve:
http://ruby-doc.org/core/classes/Kernel.html#M005955

Do jeito que você quer não conheço…

Realmente, não serve não.

Que triste :cry:

T

Gujeiros,

será que vocês poderiam citar referências bibliográficas para entendermos esse papo todo? Não, não quero nenhum livro dos Deitel. E nem precisa ser um Head First Metaprogramming. Pode mandar algo mais bizarro. :stuck_out_tongue:

Acabei de me avaliar como white-and-yellow-collar! :roll:

L

TheMask:
Gujeiros,

será que vocês poderiam citar referências bibliográficas para entendermos esse papo todo? Não, não quero nenhum livro dos Deitel. E nem precisa ser um Head First Metaprogramming. Pode mandar algo mais bizarro. :stuck_out_tongue:

Acabei de me avaliar como white-and-yellow-collar! :roll:

Procure pelo assunto no lambda the ultimate e no citeseer. Boa leitura.

F

TheMask:
Gujeiros,

será que vocês poderiam citar referências bibliográficas para entendermos esse papo todo? Não, não quero nenhum livro dos Deitel. E nem precisa ser um Head First Metaprogramming. Pode mandar algo mais bizarro. :stuck_out_tongue:

Acabei de me avaliar como white-and-yellow-collar! :roll:


Ruby for Rails do David Black também é uma boa leitura e aborda coisas interessantes sobre metaprogramação aplicada no rails.

M

Eu ainda não entendi direito o que você está tentando fazer, vc procura algo como se os contextos fossem se sobrepondo numa pilha ou uma "herança de escopo", mas ainda não entendi o que isso tem a ver com monkey patching.
O que eu pensei foi em passar o contexto que tem o método que vc qr acessar por variável.

class A
 def initialize
  @abc =122
 end

 def hehe
  B.new.huhuhu {|context| p @abc; puts context.bwawawawawa}
 end
end

class B
 def huhuhu(&block)
  yield self
 end

 def bwawawawawa
  'risada maquiavelica'
 end
end

A.new.hehe
P

O seu exemplo é basicamente aq mesma coisa do do Kung, com a direção invertida. Ainda é uma gambiarra e apesar de solucionar o exemplo (1)cria ainda mais sintaxe acidental e (2)não resolve todos os casos, e se eu quiser ter três níveis? Níveis arbitrários? A pergunta é: como resolver isso sem mexer no bloco nem na invocacão do método?

O problema é que se eu não tenho mecanismo de binding decente eu crio muitos métodos em classes existentes, como no caso que citei do Selenium onde uma classe de domínio teve que incluir um módulo do SeleniumRC. No fim do dia você acaba tendo métodos em Fixnum que ó são usados em um canto escuso de uma biblioteca que você importou. Um bom exemplo é o problema que o RSpec cria aodefinir métodos em Object. Se fosse possível definir métodos apenas dentro do bloco it() ele não precisaria poluir todo o namespace com metodos que só fazem sentido nele.

Com a ênfase em DSLs em Ruby pode-se esperar ainda mais oluição, cada DSL implementa sua gambiarra para ter uma sintaxe diferenciada e no final seu Fixnum vai estar assoviando, chupando cana e se chamando Roberta Close.

F

Phillip, que tal colocar as operações que vc quer adicionar nas classes (limitando o escopo) em um módulo e sobrescrever o append_features (http://ruby-doc.org/core/classes/Module.html#M001659):

module DSLExtensions
  def ... end
  def append_features(mod)
    #guarda as modificacoes em algum lugar para desfazer depois!
  end
end

def novo_escopo(&blk)
  Fixnum.send(:include, DSLExtensions)
  instance_eval(&blk)
  remove_extensions_from(Fixnum)
end

Gambi forte. Não sei se é pior que passar o contexto pai como variável…

P

E eu faço a mesma besteira que o Rails faz e fico com uma thread só?

Nah, a outra gambiarra pelo menos era tread-safe :stuck_out_tongue:

F

é, mas a outra tb não resolve o problema do “scoped monkey patching”!

operacao do
  1.so_pode_ter_esse_metodo_aqui
end
F

(e acabaram as minhas idéias)

(por enquanto)

A conclusão é que se eu tivesse a solução para isso, já estaria tão famoso quanto o Phillip!

P

São dois problemas. Um é a falta de opções de binding, fazendo com que eu tenha quea alterar classes (adicionar métodos em inteiros) quando o que eu queria era simplesmente que um método estivesse disponivel em um contexto qualquer. Outro é o fato de que se eu alterar uma classe (como consequência do primeiro ou não) eu altero ela em todo o sistemas.

Será que JRuby não consegue resolver o seundo problema com classloaders diferentes? Não tenho idéia, não sei como o JRuby funciona… LOOOOOOOOOOOOOOUUUUUDS!!!

L

pcalcado:
São dois problemas. Um é a falta de opções de binding, fazendo com que eu tenha quea alterar classes (adicionar métodos em inteiros) quando o que eu queria era simplesmente que um método estivesse disponivel em um contexto qualquer. Outro é o fato de que se eu alterar uma classe (como consequência do primeiro ou não) eu altero ela em todo o sistemas.

Será que JRuby não consegue resolver o seundo problema com classloaders diferentes? Não tenho idéia, não sei como o JRuby funciona… LOOOOOOOOOOOOOOUUUUUDS!!!

Acho que usando 2 instancias, uma para a aplicação e outra para a DSL, com call by value, iow serializa todos argumentos, provavelmente funciona.

M

Sou fã desse bixo :shock:

De qualquer forma, não daria conflito de classloaders ter e usar a mesma classe duas vezes no mesmo lugar não?

Normalmente quando você carrega a mesma classe por dois classloaders e tenta fazer uma atribuição já pega uma CCE.

Não sei porque mas eu ainda acho a gambiarra do call stack menos bizonha (provavelmente porque eu ainda acho que o AspectJ era uma boa idéia) :stuck_out_tongue:

T

Desculpe a ignorância, mas por que AspectJ não seria considerada uma boa idéia nesses dias?
Pergunto porque este mês comecei a estudar POA com AspectJ…

M

tnaires:
Desculpe a ignorância, mas por que AspectJ não seria considerada uma boa idéia nesses dias?
Pergunto porque este mês comecei a estudar POA com AspectJ…

Porque a necessidade de uma ferramenta de AOP pra fazer alguma coisa é um sinal de que a linguagem não oferece facilidades pra alteração das definições de objetos e classes em tempo de execução boas ou simples o suficiente.

P

Hum…acho que se voce transformar o seu bloco numa proc atraves de lambda e em seguida dar um call na proc, resolve o problema:

A sugestão para a bazooka procedeu sem problemas

def fire(&block)
  lambda(&block).call(instance_eval(&block))
end

e para as risadas malevolentes:

def huhuhu(&block)
   lambda(&block).call(instance_eval(&block))
 end

Só tem uma coisa que não entendi, para a segunda sugestão que passei, ele retorna

nil
"risada maquiavelica"
122
test.rb:7:in `hehe': undefined local variable or method `bwawawawawa' for #<A:0x2ae5c0c @abc=122> (NameError)
	from test.rb:13:in `call'
	from test.rb:13:in `huhuhu'
	from test.rb:7:in `hehe'
	from test.rb:21

porque diabos a saida está assim, acusando erro de escopo mesmo imprimindo o valor do referido método? e porque a ordem de impressão dos atributos ficou invertida?

pior nem é isso. Quando eu inverto os valores em

def hehe
  B.new.huhuhu{p bwawawawawa; p @abc}
 end

ele imprime:

"risada maquiavelica"
nil
test.rb:7:in `hehe': undefined local variable or method `bwawawawawa' for #<A:0x2ae5c0c @abc=122> (NameError)
	from test.rb:13:in `call'
	from test.rb:13:in `huhuhu'
	from test.rb:7:in `hehe'
	from test.rb:21
>Exit code: 1

Alguém consegue explicar isso?

T+

P

Glaucio, você está executando o mesmo bloco várias vezes, não? A idéia é executar uma vez só e ter acesso aos métodos necessários nesta execução. Executar o mesmo bloco diversas vezes é inviável na maioria dos cenários para uma linguagem não funcional (que tem estado mútavel).

Btw, alguém implementa uma gambi com currying?

N

Com Rubinius + MultiVM (que não sei a quantas isso anda) deve funcionar parecido não ?

F

pcalcado:
São dois problemas. Um é a falta de opções de binding, fazendo com que eu tenha quea alterar classes (adicionar métodos em inteiros) quando o que eu queria era simplesmente que um método estivesse disponivel em um contexto qualquer. Outro é o fato de que se eu alterar uma classe (como consequência do primeiro ou não) eu altero ela em todo o sistemas.

Será que JRuby não consegue resolver o seundo problema com classloaders diferentes? Não tenho idéia, não sei como o JRuby funciona… LOOOOOOOOOOOOOOUUUUUDS!!!


Se não me engano, cada classe Ruby vira um objeto de verdade na JVM. Instância de RubyClass, ou algo do tipo.

Não precisa de vários ClassLoaders pq não é estático. Bastaria fazer com que os escopos pudessem estar associados a versões modificadas do RubyClass original. Isso não deve ser muito difícil de fazer, mas eu acho que a chance de fazerem deve ser perto de nula, já que isso faria com que o JRuby não fosse compatível com o MRI.

De qq forma, acho que vale um papo com o Nutter e cia no #jruby @ freenode, ou na lista do jruby-dev.

P

pcalcado:
Glaucio, você está executando o mesmo bloco várias vezes, não? A idéia é executar uma vez só e ter acesso aos métodos necessários nesta execução. Executar o mesmo bloco diversas vezes é inviável na maioria dos cenários para uma linguagem não funcional (que tem estado mútavel).

Btw, alguém implementa uma gambi com currying?

Opa,

você tem razão, o que propus parecia trapaça para solucionar o problema, e o que é pior, nem currying para isso funciona direito porque lambda tenta processar o bloco com tudo o que tiver dentro dele o_O

O ponto é: lambda.call enxerga o escopo de A e tenta evocar o de B (dando a tal unresolved variable), instance_eval enxerga o escopo de B e não enxerga o de A.

De todo modo, o problema da bazooka pode se resumir a:

def fire(&block)
 lambda(&block).call
end

No caso do exemplo das risadas, não sei como proceder…nao sei como, mas se lambda pudesse aceitar algo tipo:

def fire(&block)
 lambda(&block, self).call #arrumem uma notação melhor :D
end

Seria interessante, pois poderiamos fazer o bloco enxergar o escopo externo ao bloco

T+

N

Proteu Alcebidiano:

def fire(&block)
 lambda(&block, self).call #arrumem uma notação melhor :D
end

Seria interessante, pois poderiamos fazer o bloco enxergar o escopo externo ao bloco

T+

Nesse caso, eu ainda gosto da idéia do binding hierárquico. :smiley:

L

Fiquei pesando no seguinte, da mesma forma que se usa o Ycomb para implementar recursão em um lambda, será que não existe como derivar o fix point dos bindings e, ao ir adiante, ir chamando tudo via algo semelhante ao Ycomb?

P

louds:
Fiquei pesando no seguinte, da mesma forma que se usa o Ycomb para implementar recursão em um lambda, será que não existe como derivar o fix point dos bindings e, ao ir adiante, ir chamando tudo via algo semelhante ao Ycomb?

Rapaz, usando a idéia de combinators em bindings seria interessante porque voce poderia mudar a regra de binding sem precisar modificar o runtime da linguagem, e considerando que bindings realizam ações (funçoes) de referência, poderia-se criar uma high-order function para bindings…algo tipo, numa implementaçao de Y-comb (pegar uma implementação em erlang, pra facilitar a leitura)

y(X) -&gt;
  F = fun (P) -&gt; X(fun (Arg) -&gt; (P(P))(Arg) end) end,
  F(F).

voce ter uma funcao de counting ou binding regstry, que seria acionada pelo analisador a cada vez que um comando dentro de um bloco não estivesse definido no escopo mais "raso" do código:

% chamem o rapazinho aqui para registrar bindings
count_binding() -&gt;
 F=fun (SUM) -&gt; fun (N) -&gt;
  %registrar uma contagem ou qualquer tralha que  uma chance ao código do bloco, exemplo
  %SUM(N) -&gt; N + SUM(N-1)
  %SUM(1) -&gt; N.
  end,
  end
  y(F).

ou então, se não quiser fazer algo hierárquico, colocar numa hash ou lista ao invés de um contador.

Algo assim procede ou tou viajando? =D

T+

L

Olá

Também acho arriscado permitir que isto aconteça.

[]s
Luca

P

Esse artigo me deixou bem interessado em COP. Pena que a leitura vai ter que esperar um bom tempo na fila…

Criado 25 de fevereiro de 2008
Ultima resposta 23 de mar. de 2008
Respostas 72
Participantes 15