• [^] # Re: Petite erreur dans le texte de la dépêche

    Posté par . En réponse à la dépêche Atelier Actux mardi 19/05/2009 : Ruby, un langage objet et (assez) fonctionnel. Évalué à 9.

    Non, le mot fonctionnel lorsque l'on parle d'un langage de programmation décrit un paradigme (comme orienté objet ou impératif).
    L'idée de base, c'est que les fonctions sont des valeurs dans ce type de langage (à titre de comparaison, on a l'habitude en programmation de pouvoir manipuler des nombres, des chaînes de caractères, des tableaux, et parfois même des regexs comme des valeurs --- mais plus rarement des fonctions). C'est un style qui est assez jouissif à utiliser, parce qu'il permet de mieux factoriser son code et de le rendre plus sémantique (plus expressif).

    L'exemple typique pour illustrer l'aspect sémantique, c'est la fonction map() qui applique une fonction à chaque élément d'une collection (un tableau, une liste). Dans les langages impératifs, on utilise une boucle for pour ce genre de tâches :
    for (int i = 0; i < tab.length; ++i)
    tab[i] = f(tab[i]);

    Et on ne voit aucune raison de factoriser deux lignes de code... sauf qu'en pratique, l'exemple précédent ne se présente pas comme ça, mais plutôt comme :
    for (int i = 0; i < tab.length; ++i) {
    // plusieurs
    // lignes
    // de
    // code
    }

    Et il devient alors plus difficile de comprendre ce qui se passe, parce qu'un tas de facteurs peuvent influer dans le corps de la boucle (on peut modifier la valeur de l'itérateur, on peut décider de ne pas modifier à chaque fois le tableau, ou de faire d'autres opérations, ...).
    Bref, la boucle for est générale, mais un peu trop pour faciliter la compréhension. Ce qu'il faudrait, c'est une boucle plus sémantique qui dit au programmeur "alors là tu vois, je vais mettre à jour toutes les cases du tableau", ce qui permet d'éviter de nombreux bugs (et on peut appliquer le même principe aux autres sortes de boucles que l'on retrouve souvent). Bref, on construit des briques expressives qui décrivent une action.
    En ruby,
    irb(main):001:0> tab = [1,2,3]
    irb(main):002:0> tab.map { |x| x * x }
    => [1, 4, 9]


    Un exemple plus concret, c'est pour les GUIs : Lorsque l'on crée un bouton, il faut pouvoir spécifier son comportement lors d'un clic. La solution pseudo-OO serait de faire hériter son bouton de la classe Button et de redéfinir la méthode on_click() et finalement de l'instancier... mais c'est un peu lourd de faire ça pour chaque bouton (à mon goût).
    À comparer avec le code suivant :

    input = Textarea.new
    bouton = Button.new
    bouton.on(:click) do
    puts input.value
    end


    Un autre exemple, pour les fanas d'algorithmie : Si l'on a un graphe que l'on souhaite parcourir en largeur (pour chercher un élément, ou autre), le code de parcours est assez général (c'est l'action sur chaque élément qui diffère). On peut donc souhaiter programmer une seule fois le parcours et laisser libre le choix de l'action au programmeur ensuite.
    Un exemple d'utilisation :
    def has_value(graph, x)
    graph.dfs do |value|
    return true if x == value
    end
    return false
    end


    Bref, les possibilités offertes par le paradigme fonctionnel sont très riches et ouvrent de nouvelles opportunités. Entre autre, je pense que c'est un style qui permet de mieux éviter les erreurs parce qu'il permet une meilleure découpe des différents composants (dans l'exemple du dfs, par exemple, si une erreur est décelée, il est facile de voir que has_value() n'est pas à remettre en cause -- et que l'erreur se trouve dans la méthode dfs()). Le code tend à être plus "simple" (mais c'est relatif, car il est aussi plus abstrait).

    Il ouvre également des portes pour les optimisations : Par exemple, pour map(), puisque l'on sait que chaque case du tableau va être mise à jour indépendamment des autres, il est possible de paralléliser trivialement les calculs sur plusieurs bécanes. Oui oui, le map dans MapReduce (de google) vient de là (et le reduce correspond à un autre type de boucle).

    Finalement, l'arrivée du fonctionnel dans les langages comme C++, C# et les différents langages de script populaires (Ruby, Python, Lua...) montrent bien qu'il s'agit d'un concept intéressant.

    Remarque: les langages implémentent plus ou moins bien le(s) paradigme(s) sur le(s)quel(s) ils reposent (par exemple, Java n'est pas suffisamment OO à mon goût). Ruby est "assez" fonctionnel dans le sens qu'il supporte l'idée, mais pas aussi bien que des vrais langages fonctionnels. Je recommande donc OCaml si vous voulez vous essayer à ce style langage :) .