Dans quel domaine la macro de LISP est-elle meilleure que la «capacité» de Ruby à créer DSL

21

L'une des choses qui fait briller Ruby est la possibilité de mieux créer des langages spécifiques au domaine, comme

Bien que l'on puisse dupliquer ces bibliothèques dans LISP via une macro, je pense que l'implémentation de Ruby est plus élégante. Néanmoins, je pense qu'il y a des cas où la macro de LISP peut être meilleure que celle de Ruby, bien que je ne puisse pas en penser un.

Alors, dans quel domaine la macro de LISP est-elle meilleure que la "capacité" de Ruby à créer DSL, le cas échéant?

mise à jour

Je l'ai demandé parce que les langages de programmation modernes approchent de la singularité LISP , comme

  • C a un préprocesseur d'extension de macro , bien que très primitif et sujet aux erreurs
  • C # a des attributs, bien que ce soit en lecture seule, exposé par réflexion
  • Python a ajouté un décorateur, qui peut modifier le comportement de la fonction (et de la classe pour la version 3.0), mais semble assez limité.
  • Ruby TMTOWTDI qui rend DSL élégant, si des soins sont appliqués, mais de manière Ruby.

Je me demandais si la macro de LISP n'est applicable qu'à des cas spéciaux et que les autres fonctionnalités du langage de programmation sont suffisamment puissantes pour élever l'abstraction pour relever les défis actuels du développement logiciel.

OnesimusUnbound
la source
4
Je ne sais pas trop pourquoi il y a des votes serrés à ce sujet. Je pense que c'est le genre de question que nous voulons ? Intéressant, suscitant la réflexion et la portée est assez bien définie.
Tim Post
Essayez d'implémenter quelque chose comme ça dans Ruby: meta-alternative.net/pfront.pdf
SK-logic
@Tim Post: Un problème est que ce n'est vraiment pas une question facile à répondre à moins que vous ne connaissiez bien Common Lisp et Ruby. Un autre est les références assez ignorantes à d'autres langages, qui peuvent agacer les puristes: il n'y a pas de langage appelé C / C ++, et la partie significative de C ++ est le système de modèles, et la suggestion que le système de macros de Common Lisp ne s'applique qu'à des cas spéciaux. Fondamentalement, c'est une bonne question, mais elle est mal écrite et il est difficile d'y répondre.
David Thornley
@David Thornley, j'ai mis à jour l'article, en particulier sur C \ C ++, et j'ai mis l'accent sur C. En ce qui concerne Common Lisp, je sentais que puisque d'autres langages de programmation ont une abstraction plus élevée, sa fonction macro est pour un cas spécial. J'ai posté la question, en espérant que d'autres me montreront que la macro de CL n'est pas pour un cas spécial est toujours une fonctionnalité puissante.
OnesimusUnbound
@OnesimusUnbound: J'inclurais vraiment les modèles de C ++ dans votre liste d'exemples de langage, car ils sont au moins théoriquement capables de tout calcul que le système de macro Lisp peut faire. En ce qui concerne les macros de Lisp, saisissez du bon code Common Lisp (il y a beaucoup de code F / OS Lisp) et recherchez "defmacro" (vous voudrez peut-être faire une recherche non sensible à la casse). Vous en trouverez probablement beaucoup. Considérez que les macros sont plus difficiles à écrire et à faire que les fonctions, et vous vous rendrez compte qu'elles doivent être généralement utiles.
David Thornley

Réponses:

23

Lisez sur Lisp et décidez par vous-même.

Mon résumé est que Ruby est mieux à même de fournir une syntaxe pratique. Mais Lisp gagne, haut la main, à la capacité de créer de nouvelles abstractions, puis de superposer l'abstraction sur l'abstraction. Mais vous devez voir Lisp en pratique pour comprendre ce point. D'où le livre recommande.

btilly
la source
1
"Let Over Lambda" couvre un terrain très similaire. Lecture également recommandée.
John R. Strohm
But Lisp wins, hands down, at the ability to create new abstractions, and then to layer abstraction on abstraction.Maintenant je sais pourquoi. . .
OnesimusUnbound
Ce n'est pas grave de fournir une syntaxe quelconque en plus de Lisp. En fait, beaucoup plus facile qu'avec Ruby, grâce aux macros de lecture.
SK-logic
1
@ SK-logic: Vrai. Cependant, Lispers évite les macros de lecture, car une fois que vous suivez cette voie, il ne ressemble plus à un Lisp. En fait, certaines variantes de Lisp, comme Clojure, réservent des macros de lecture au concepteur de langage.
btilly
14

Les installations de Ruby pour la création DSL ne changent pas la nature de la langue. Les installations de métaprogrammation de Ruby sont intrinsèquement liées à la syntaxe et à la sémantique de Ruby, et tout ce que vous écrivez doit être shunté dans le modèle objet de Ruby.

Comparez cela avec Lisp (et Scheme, dont les fonctionnalités de macro diffèrent ), où les macros opèrent sur le programme abstrait lui-même. Parce qu'un programme Lisp est une valeur Lisp, une macro est une fonction mappant une syntaxe essentiellement arbitraire à une autre.

En effet, un Ruby DSL ressemble toujours à Ruby, mais un Lisp DSL n'a pas à se sentir comme Lisp.

Jon Purdy
la source
6
+1: LOOP ne se sent pas très Lispy, comme un exemple de DSL.
Frank Shearar
2

Les DSL de Ruby ne sont pas du tout des DSL, et je déteste absolument les utiliser parce que leur documentation repose sur leur fonctionnement réel. Prenons l'exemple d'ActiveRecord. Il vous permet de «déclarer» des associations entre les modèles:

class Foo < ActiveRecord::Base
    has_one :bar
    has_one :baz
end

Mais le caractère déclaratif de ce "DSL" (comme le caractère déclaratif de la classsyntaxe de Ruby elle-même) est un horrible mensonge qui peut être exposé par quiconque comprend comment les "DSL" Ruby fonctionnent réellement:

class Foo < ActiveRecord::Base
    [:bar,:baz,:qux,:quux].each do |table|
        has_one table if i_feel_like_it?(table)
    end
    puts "Just for shits and giggles, and to show"
    puts "just how fucked up Ruby really is, we're gonna ask you"
    puts "which SQL table you want the Foo model to have an"
    puts "association with.\n"
    puts "Type the name of a table here: "
    has_one gets.chomp.to_sym
end

(Essayez simplement de faire quelque chose de proche dans le corps d'un defclassformulaire Lisp !)

Dès que vous avez du code comme celui-ci dans votre base de code, chaque développeur du projet doit comprendre pleinement comment les DSL Ruby fonctionnent réellement (et pas seulement l'illusion qu'ils créent) avant de pouvoir maintenir le code. La documentation disponible sera totalement inutile, car elle ne documente que l' usage idiomatique , ce qui préserve l'illusion déclarative.

RSpec est encore pire que ce qui précède, car il a des cas de bord bizarres qui nécessitent une ingénierie inverse approfondie pour déboguer. (J'ai passé une journée entière à essayer de comprendre pourquoi l'un de mes cas de test était ignoré. Il s'est avéré que RSpec exécute tous les cas de test qui ont des contextes après des cas de test sans contexte, quel que soit l'ordre dans lequel ils apparaissent dans la source , car la contextméthode place votre bloc dans une structure de données différente de celle qu'il utiliserait normalement.)

Les DSL Lisp sont implémentés par des macros, qui sont de petits compilateurs. Les DSL que vous pouvez créer de cette façon ne sont pas de simples abus de la syntaxe existante de Lisp. Ce sont de véritables mini-langues qui peuvent être écrites de manière totalement transparente, car elles peuvent avoir leur propre grammaire. Par exemple, la LOOPmacro de Lisp est beaucoup plus puissante que la eachméthode de Ruby .

(Je sais que vous avez déjà accepté une réponse, mais tous ceux qui liront ne voudront pas lire l'intégralité de On Lisp .)

Jetez votre compte
la source