Efeitos Colaterais e Códigos

Esse assunto é bem complexo, e envolve certas situações interessantes. Mas antes, precisamos de uma definição do que é um efeito colateral em código: quando se escreve um código em uma linguagem, cada procedimento/função/método deveria fazer uma, e apenas uma tarefa. Mais do que isso, e estamos inserindo um efeito colateral no código. Quando programamos em linguagens puramente funcionais, como por exemplo Haskell, fica evidente que estamos mexendo com uma função com efeitos colaterais porque ela fica, de certa forma, “marcada”.

Ok, e em Ruby, por exemplo? Como saber? A idéia é simples (e talvez isso possa até ser uma definição formal): “Uma função não tem efeitos colaterais quando não importa quantas vezes ela é chamada, ela sempre traz os mesmos resultados”. Embora simples, na teoria, identificar uma função assim nem sempre é fácil. Por exemplo, imaginemos um caso bem interessante: a função “save” do ActiveRecord. Ela parece, em teoria, não ter efeitos colaterais, mas ela possui um: vamos para um código na prática:

class Person < ActiveRecord::Base
  validates_uniqueness_of :name
end

me = Person.new :name => "Maurício"
myself = Person.new :name => "Maurício"
me.save #true.
me.save #false. FALSE?

Tecnicamente falando, as duas funções deveriam retornar true: eu passei dois objetos exatamente iguais para elas. O segredo, se é que há algum, é que o método “save” na verdade faz duas coisas: valida os objetos e salva-os no banco. O principal problema é o “salvar no banco”, na verdade: estamos definindo um estado “global”, digamos assim. Todos os nossos novos objetos “Person” vão, automaticamente, ter que consultar o banco (nosso “estado global”) e verificar se a propriedade “name” já existe. Como se, por causa desse “estado” todos os saves automaticamente ganham um “if” a mais. Outra coisa também que o “save” faz que indica um efeito colateral é mudar o “id” do objeto:

me = Person.new :name => "Maurício"
p me.id #retorna nil
me.save
p me.id #retorna um número qualquer

(more…)