segunda-feira, 25 de julho de 2011

Pequenas coisas que incomodam em Ruby

Ruby é uma linguagem maravilhosa, mas tem alguns poréns. O que eu vou citar é algo bem pequeno mas a linguagem está repleta de coisas do tipo.

> class Foo
>   def bar; @bar; end
>   def bar=(other)
>     puts "bar= has been called"
>     @bar = other
>   end
> end

Basicamente escrevemos um setter para um variável.

> foo = Foo.new
> foo.bar = 1
bar= has been called
=> 1 

Ok, exatamente como esperávamos.

> foo.bar = foo.bar || 2
bar= has been called
=> 1 

Ok, sem surpresas ainda.

> foo.bar ||= 2
=> 1 

Ops, notem que bar= não foi chamado.

Portanto, ao contrário do que muitos pensam foo.bar ||= 2 é diferente de foo.bar = foo.bar || 2.

foo.bar ||= 2 só realiza a atribuição se bar não avaliar para uma expressão verdadeira.

É algo muito bobo e muito sútil, mas se o programador realizar alguma computação na atribuição, essa computação pode não ser executada em alguns casos.

Isso não é um grande problema, porém existem diversas coisas do tipo na linguagem. O Matz diz que a linguagem deve ter o comportamento que o programador espera, isso é bom porque a linguagem torna-se natural, mas e casos como esse, o que o programador espera?

Outra coisa decorrente é que isto dificulta outras implementações Ruby 100% compatíveis.

4 comentários:

  1. Acho que essa confusão é gerada por comparação aos operadores matemáticos += -= ...

    value = 1
    value += 1 # same value= value + 1

    O operador binário || diz que, se o operando a esquerda for um valor verdadeiro, retorna este valor, mas caso contrário se executa o operador a direita, que neste caso tem um sugar sintaxe para chamar o atribuidor (=) do valor a esquerda, que neste caso o operando a esquerda deve implementar o método (=)

    value ||= 2 # same value || value = 2

    ResponderExcluir
  2. Sei não, nesse específico caso a linguagem se comporta como eu espero, então, pra mim causa o efeito oposto. São surpresas desse tipo que me maravilham todo dia.

    Mas fiquei interessado em saber sobre as outras coisas que te incomodam no Ruby.

    ResponderExcluir
  3. Discordo. A linguagem se comporta exatamente como eu esperaria.
    Como disse, o Celestino, talvez tenha havido uma confusão pelo fato de ||= parecer com +=. Na verdade:

    `foo.bar ||= 2` é apenas um atalho para `foo.bar = 2 unless foo.bar`

    ResponderExcluir
  4. Correção:

    `foo.bar ||= 2` é na verdade um atalho para `foo.bar || foo.bar = 2`

    ResponderExcluir

Comenta ae, amiguinho!