quinta-feira, 27 de janeiro de 2011

Um novo Object#tap

Implementação atual de tap:
class Object
  def tap
    yield(self)
    self
  end
end
Mas o Daniel aqui discorda. Minha proposta é:
class Object
  def tap
    yield(self)
  end

  def tee
    yield(self)
    self
  end
end
Object#tap
altera a method chain
Object#tee
não altera a method chain (atual Object#tap)
Para Enumerable, nós temos Enumerable#collect, que funciona exatamente igual ao meu tap.
[1,2,3].collect {|i| i + i} => [2,4,6]
Porém não temos isso para objetos:
1.tap {|i| i + i } => 2
Object#tap executa um bloco na method chain sem altera-la.
[1,2,3].collect {|i| i * i }.tap {|a| puts a.size }.inject {|sum, i| sum + i } => 14 (escreve 3 na STDOUT antes do retorno)
Porém se você quer justamente passar um bloco apenas para alterar a method chain? Não deveria ser justamente essa a função de Object#tap?
[1,2,3].collect {|i| i * i }.tap {|a| a + a }.inject {|sum, i| sum + i } => 28
A origem do atual Object#tap vem do comando tee. Portanto, nada mais justo que:
class Object
  alias tee tap
  def tap
    yield(self)
  end
end
É preciosismo e a discussão é meramente teórica, já que podemos implementar este novo Object#tap com outro nome e evitar o alias, o que exigiria um refactoring de todo código que utiliza Object#tap. Possíveis nomes para meu Object#tap: get, come, become, go e grow. Viajei muito?