Ruby é JavaScript ao Avesso

O titulo é uma brincadeira mas é uma boa forma de lembrar algumas coisinhas sobre programação nestas linguagens. Cada vez mais lidamos no dia-a-dia com conceitos que estão presentes há décadas em linguagens mais esotéricas mas nunca deram as caras no mainstream, um deles é o uso de funções como abstração. Existe um conflito de termos aqui então só para deixar claro eu não estou falando de funções como em programação procedural mas sim de funções como vemos em closures.

Muita gente tem escrito sobre como devemos aprender programação funcional. Eu concordo mas não posso deixar de notar que quando alguém diz programação funcional geralmente ela quer dizer Higher-Order Functions.

E o que é isso? Bom, uma linguagem possui higher-order quando uma função pode receber como parâmetro outra função. O nome deriva do fato de que uma função que recebe outra é considerada de ordem 1, uma função que recebe outra que recebe outra é considerado 3 e assim em diante.

JavaScript possui higher-order programming. Funções são a abstração principal em javaScript e elas podem ser passadas à vontade pelo programa. Por exemplo, vamos supor que queremos comparar dois objetos de acordo com um critério arbitrário. Em JavaScript podemos fazer algo assim:

function melhorEntre(um, outro, criterio){
  if(criterio(um, outro)){
    return um;
  }
  else {
    return outro;
  }
}

sorvete1 = {sabor: 'morango'};
sorvete2 = {sabor: 'chocolate'};

prefiroChocolate = (function (s1, s2){
                                   return (s1.sabor === 'chocolate');
                            });

prefiroMorango = (function (s1, s2){
                                  return (s1.sabor === 'morango');
                            });

alert(melhorEntre(sorvete1, sorvete2, prefiroMorango).sabor);
alert(melhorEntre(sorvete1, sorvete2, prefiroChocolate).sabor);

Em Ruby o código ficaria um pouco diferente:

def melhor_entre(um, outro, criterio)
    if criterio.call(um, outro)
        um
    else
        outro
    end
end

sorvete_1 = { :sabor => 'chocolate' }
sorvete_2 = { :sabor => 'morango' }

prefiro_chocolate = lambda {|s1,s2| s1[:sabor] == 'chocolate'}
prefiro_morango = lambda {|s1,s2| s1[:sabor] == 'morango'}

puts melhor_entre(sorvete_1, sorvete_2, prefiro_morango)[:sabor]
puts melhor_entre(sorvete_1, sorvete_2, prefiro_chocolate)[:sabor]

Agora vamos pensar: as duas linguagens possuem higher-order programming? Não, só JavaScript possui. Em Ruby o que é passado não é uma função e sim um objeto, veja só:

prefiro_chocolate = lambda {|s1,s2| s1[:sabor] == 'chocolate'}
puts prefiro_chocolate
#=> #<Proc:0x00028a64@tmp/compara.rb:19>
puts prefiro_chocolate.class
#=> Proc

O principal divergente da solução em Ruby é que você deve passar a mensagem call para o objeto (ou usar a palavra-chave yield). Na pratica do dia-a-dia não tem tanta diferença e é comum falar em higher-order Ruby.

Em Ruby não precisamos de funções de verdade para termos higher-order programming, podemos usar objetos para modelar as funções. Em JavaScript não temos construções especiais para objetos, mas utilizamos funções:

function Sorveteiro(){
  this.numeroDeVendidos = 0;
  this.vender = function(){this.numeroDeVendidos++;};
};

s = new Sorveteiro();
alert(s.numeroDeVendidos);
s.vender();
alert(s.numeroDeVendidos);
s.vender();
s.vender();
alert(s.numeroDeVendidos);

Alguém me disse essa semana que uma das grandes vantaens em aprender higher-order programming (a pessoa falou em programação funcional mas não é bem isso que ela quis dizer) é que com ela você simula objetos mas o contrario não é verdade. Bom, não é assim. Nada impede de você ter higher-order programming em uma linguagem Orientada a Objetos (JavaScript é Orientada a Objetos!) e com objetos você pode facilmente modelar higher-order programming.

E qual a diferença disso tudo para programação funcional? Bom, programação funcional usa higher-order programming, mas não é isso que define uma linguagem funcional (JavaScript não é funcional).

Em 1984, John Hughes publicou um paper chamado “Why Functional Programming Matters”. Este paper é, até hoje, uma das obras mais importantes para o paradigma. Nele o autor descreve:

The special characteristics and advantages of functional programming are often summed up more or less as follows. Functional programs contain no assignment statements, so variables, once given a value, never change. More generally, functional programs contain no side-effects at all. A function call can have no effect other than to compute its result. This eliminates a ma jor source of bugs, and also makes the order of execution irrelevant - since no side-efect can change the value of an expression, it can be evaluated at any time. This relieves the programmer of the burden of prescribing the flow of control. Since expressions can be evaluated at any time, one can freely replace variables by their values and vice versa - that is, programs are “referentially transparent”. This freedom helps make functional programs more tractable mathematically than their conventional counterparts.

Erik Meijer apresentou uma palestra no JAOO chamada “Why Functional Programming (still) Matters” onde ele afirma que nenhuma linguagem é realmente funcional, provando que se pode simular efeitos colaterais em Erlang, Haskell, F# e várias outras.

A conclusão do Erik -é claro que defendendo suas decisões ao criar o LINQ- é que os conceitos por trás das linguagens ditas funcionais são mais importantes do que ser uma linguagem puramente funcional ou não.

Isso significa que você deve aprender sobre programação funcional e aplicar suas técnicas sempre que necessário mas cuidado com o termo “funcional”. Na maioria das vezes você quis dizer Higher-Order Programming.

Update: Alguns dos comentários msotram uma confusão com funções javaScript e objetos. Não só o texto falou que em JavaScript funções são objetos bem como ele mostrou o exemplo do sorveteiro, mas tentando deixar ainda mais claro:

ds

18 Responses to “Ruby é JavaScript ao Avesso”

  1. Leandro says:

    Bom, ótimo!!! Ver textos como esse deixando mais claros alguns pontos e confundindo mais outros pontos. Quando digo confundindo é no sentido de ignorância mesmo, falta de conhecimento (meu) da totalidade de assuntos tratados, mas essa mesma ignorância que faz a busca para que o confuso fique claro. (ficou confuso :S )
    Resumindo textos assim me ajudam a evoluir e buscar entender mais e mais coisas.

  2. Felipe Carvalho says:

    Ótimos exemplos, explicam muito bem.

    Estou aprendendendo Lisp e Ruby e realmente não se pode dizer que Commom Lisp, Scheme é “absolutamente” funcional.

    Concordo com o Erik: importante são os conceitos por trás das linguagens funcionais.

    Dos slides:
    “Only one way to be pure,
    But many ways to be impure”

  3. Fabio Kung says:

    Boa Phillip!
    Vou ser só um pouco _chato_. ;-)

    As funções em JavaScript podem ser vistas sim como objetos, exatamente como Ruby. O que me diz de coisas como essas:

    var f = new Function(”alert(’hey’)”)
    f()

    Os parênteses da chamada da função, poderiam ser vistos apenas como o nome de um método. Provavelmente não seja um método de verdade, já que não sei se dá para reescrevê-lo, mas poderíamos deixar a coisa quase idêntica ao que se faz em Ruby:

    function a() {
    alert(”a”)
    }
    a.call = function() {
    return this()
    }
    a.call()

    Funções em javascript tanto são objetos que podemos adicionar comportamento, não?

    Eu sei que o foco do seu texto nem foi esse, não to querendo desvirtuar a coisa. No fundo é tudo igual, javascript e ruby são parecidas demais.

  4. Oi, Kung,

    Eu sei que dá para deixar idêntico ao que você usa em Ruby, este não é o ponto. O ponto é que não dá para fazer o contrário e deixar Ruby idêntico ao que faz em JavaScript, em Ruby você vai precisar definir um objeto e passar uma mensagem em específico. O exemplo do sorveteiro mostra exatamente isso, como uma função é um objeto podemos definir comportamento nela.

    Não importa como é implementado, JavaScript possui funções e Ruby não. Em javaScript uma função pode ser executada, em Ruby um objeto não pode ser executado, ele recebe uma mensagem e executa o código correspondente.

    []s

    nota: Houve um comentário muito interessante logo antes desse mas infelizmente o autor não colocou seu nome e este blog não aprova comentários anônimos. De qualquer modo a resposta para este comentário anônimo seria basicamente a mesma: eu não falei que em JavaScript funções não são objetos, até mesmo dei exemplo de como isso é feito.

    E não, Proc não passa código executável, não em nível de linguagem -modelo computacional é algo diferente e não abrangido nesse texto- ele passa uma referência para um objeto

  5. Phillip,

    Muito legal essa definição de linguagens funcionais. Antes de você ir pro futuro, você me disse que Lisp e Prolog não eram funcionais, mas não explicou porque. Desde então eu me perguntava porque você disse isso. Agora entendo o que você quis dizer. :)

  6. Pedro Prossi says:

    Seria “C” uma linguagem funcional já que podemos passar e receber ponteiros para funções?

  7. Javascript é o que há!

  8. Shoes, nos comentários você afirma que não falou que funções não são objetos no Javascript, mas nesse trecho voce afirma algo que dá a entender isso:

    “Funções são a abstração principal em javaScript e elas podem ser passadas à vontade pelo programa”

    Na verdade funções não são a abstração principal e sim objetos literais, o Function é um objeto nativo (inclusive abaixo do objeto global Object) e se comporta como todos os outros objetos nativos segundo a spec.
    O function representa high-orders e para implementar isso eles tiveram que construir a linguagem com comportamento de linguagens funcionais para poder construir o conceito que o tipo Function representa.

    Outro trecho:

    “Em JavaScript não temos construções para criar objetos, mas utilizamos funções para modelar os objetos”

    Na verdade o que existme são construtores e Function é um tipo que usa construtor. A diferença é que o construtor do tipo Function tem certas particularidades assim como o Array implementado nativamente tem comportamento diferente de outro construtor qualquer.
    Existem senão me engano 7 tipos nativos (estou com preguiça de pesquisar na spec), Array, Function, RegEx, Function, Math, Date e String e todos representam conceitos que são implementados com comprotamento único, mas mesmo possuem construtor.
    Voce pode alegar que Array e outro tipo qualquer tem comportamento no construtor semelhante a Function, mas é decorrencia das implementações e não indicam que Array é um function.

    Na spec tenho quase certeza que define que ECMAscript é apenas OO, mas nas implementações todo mundo concorda que ela é multiparadigma, até porque para Function representar a essencia do que é definido teria que fazer closure e currying e faz mais que isso, já tem implementação com List Comprehensions e quine por exempo.

  9. larguei a preguiça um pouco, abri a spec aqui e vi que esqueci de Boolean, Error e alguns subtipos de Error, dê um desconto :)

  10. Oi, Milfont,

    Funções -e suas eventuais derivadas- são a unidade principal, não importa se são objetos ou não. Quando você cria um programa em javaScript você lida a maior parte do tempo com elas.

    Funçõs -assim como classes em Ruby ou Java- não são objetos literais, são especificações de objetos. Eu não falei que Array seja uma Function nem que tudo que existe em javaScript são functions (ser principal não quer dizer único). De qualquer modo mudei o trecho para:

    ” Em JavaScript não temos construções especiais para objetos, mas utilizamos funções:”

    Eu não entendi porque JavaScript seria multi-paradigma. A spec define ela como OO-based mas isso provavelmente é uma herança maldita do marketing de VB. Você não precisa de multiparadigma para closures -basta ter regras de escopo decente, que javaScript não tem- nem currying.

    []s

  11. Parabéns.

    Curioso como ainda existem dúvidas em relação à definição de tipos no JavaScript.

    []s

  12. Carlos Alexandre Moscoso says:

    “Erik Meijer afirma que nenhuma linguagem é realmente funcional, provando que se pode simular efeitos colaterais em Erlang, Haskell, F# e várias outras.”

    Na verdade ele diz que não é puramente funcional.

    Achei interessante isso:
    “Lazy evaluation is the most powerful tool for modularisation in the functional programmer’s repertoire.”

  13. Carlos, “puramente” funcional é um termo estranho. Como alo pode ser funcional e não ser puramente uncional? Eu sei que termo é usado mas eu não concordo com ele.

    De quaquer modo, mais que os slides eu assitis à paestra dele em Sydney e tive oportunidae de conversar durante o evento.

    []s

  14. No limite, um programa em uma linguagem puramente funcional seria basicamente um aquecedor de ambiente muito caro.

  15. Sei nao, heim Rafael. Aquecer o ambiente é I/O :D

  16. Não entendi nada do que você falou, mas gostei do título e só por isto, pela primeira vez, acho, vim aqui comentar. E gostei do título pois me lembrou uma música do Kaiser Chiefs que gosto e se chama Ruby e também por me fazer procurar na gramática (leia-se internet) um termo que usamos muito em história que é às avessas, que no final é tudo a mesma coisa, mas fiquei curiosa com a origem das palavras.

    beijin (g) Já me preparando para as olimpíadas. :P

  17. Leo Gomes says:

    Tem uma cantora francesa que tb tem uma música chamada Ruby http://letras.terra.com.br/camille/437655/, composição do Euston Jones.

  18. [...] Phillipe Calçado escreveu um excelente post aqui. O Leandro Silva também escreveu um bem legal aqui. Postado em Dojo, linguagens. Tags: Dojo, [...]