1 points par GN⁺ 2024-10-18 | 1 commentaires | Partager sur WhatsApp
  • Le texte de 1983 de Douglas Hofstadter présente Lisp, largement utilisé dans la recherche en IA, comme un langage élégant et flexible bâti sur un petit noyau mathématique
  • L’interactivité de Lisp apparaît dans la read-eval-print loop : l’utilisateur saisit une expression, l’interpréteur la lit, l’évalue, l’affiche, puis attend l’entrée suivante
  • Les objets Lisp sont structurés autour des atomes (atom) et des listes (list) ; nil occupe une position particulière, étant à la fois la liste vide et un atome
  • quote, eval, car, cdr, cons, setq, set, lambda, def, cond permettent de traiter le code et les données avec la même structure de listes, et ainsi de construire progressivement de nouvelles fonctions
  • La composition de fonctions, les expressions conditionnelles, les effets de bord et le style applicatif, ainsi que la pensée récursive, sont reliés par des exemples ; la fonction finale power et la métaphore du « porpuquine » suggèrent la récursion à la Lisp

La recherche en IA et la place de Lisp

  • En 1983, l’IA était un domaine de recherche visant à implémenter sur ordinateur des comportements tels que la flexibilité, le bon sens, l’intuition, la créativité, la conscience de soi ou l’humour
  • Aux États-Unis, environ 2 000 personnes travaillaient spécifiquement sur l’IA, et il existait à l’étranger une communauté de chercheurs d’une taille comparable
  • Les chercheurs en IA étaient très divisés sur la meilleure voie pour parvenir à l’IA, mais, pour le choix du langage de programmation, ils utilisaient presque tous Lisp
  • Le nom Lisp vient de « list processing » et n’est pas un acronyme complet
  • L’attrait de Lisp tient au fait qu’il s’agit d’un langage « crisp » et « elegant »
    • Là où beaucoup de langages accumulent de nombreuses fonctionnalités arbitraires, certains langages comme Lisp et Algol sont organisés autour d’un noyau naturel, à la manière d’une branche des mathématiques
    • Le noyau de Lisp possède une « crystalline purity », et cette pureté contribue non seulement à son esthétique, mais aussi à sa flexibilité

De la logique mathématique au langage de traitement de listes

  • Les racines profondes de Lisp se trouvent dans la logique mathématique
    • Des idées laissées par Thoralf Skolem, Kurt Godel et Alonzo Church dans la logique des années 1920 et 1930 ont ensuite été intégrées à Lisp
  • La programmation informatique à proprement parler a commencé dans les années 1940, et les langages de haut niveau comme Lisp sont apparus dans les années 1950
  • Le premier langage de traitement de listes n’était pas Lisp, mais IPL (Information Processing Language)
    • IPL a été développé au milieu des années 1950 par Herbert Simon, Allen Newell et J. C. Shaw
  • John McCarthy a créé entre 1956 et 1958, à partir d’idées existantes, un langage algébrique de traitement de listes appelé Lisp
    • Lisp s’est rapidement diffusé parmi les jeunes chercheurs autour du MIT Artificial Intelligence Project
    • Il a été implémenté sur IBM 704, puis s’est propagé à d’autres groupes de recherche en IA
    • Plusieurs dialectes sont apparus, mais ils partageaient un noyau central élégant

Lisp interactif et REPL

  • Contrairement à beaucoup d’autres langages de haut niveau, Lisp est présenté comme un langage interactif
  • L’utilisateur lance Lisp dans un terminal, voit une invite, puis saisit une expression
    • Exemple : si l’on saisit (plus 2 2), 4 est affiché et une nouvelle invite apparaît
  • Le fonctionnement central de l’interpréteur Lisp est la read-eval-print loop
    • Lire l’expression
    • Évaluer l’expression
    • Afficher la valeur appropriée
    • Signaler qu’il est prêt à lire l’expression suivante
  • Grâce à cette structure, l’utilisateur peut saisir ses « souhaits » un par un et vérifier immédiatement le résultat
  • Dans de nombreux langages non interactifs, il faut d’abord écrire tout le programme puis l’exécuter ; plusieurs étapes doivent donc s’emboîter parfaitement, et la correction des erreurs est indirecte et coûteuse
  • Lisp rend possibles le développement et le débogage incrémentaux en exécutant les expressions une par une

Notation polonaise et parenthèses

  • Les expressions arithmétiques de Lisp utilisent la notation polonaise, où l’opérateur précède les opérandes
    • Exemple : (times (plus 6 3) (difference 6 3)) s’évalue à 27
  • Cette notation a été inventée avant l’ordinateur par le logicien polonais Jan Lukasiewicz
  • Les expressions Lisp contiennent beaucoup de parenthèses
    • Il est fréquent que de longues expressions se terminent par plusieurs parenthèses fermantes
    • Au début, cela peut sembler lourd, mais, avec l’habitude, la structure logique devient intuitive
  • Les exemples sont présentés sous forme de pretty printing, avec l’indentation qui fait ressortir la structure

Atomes, listes, nil

  • Le cœur de Lisp est constitué de structures manipulables, et tous les programmes fonctionnent en créant, modifiant et détruisant des structures
  • Il existe deux types de structures
    • Atome (atom) : objet de base qui ne se subdivise pas davantage
    • Liste (list) : structure regroupant plusieurs éléments dans un ordre donné
  • Tout objet Lisp est soit un atome, soit une liste, avec une seule exception : nil
    • nil est à la fois un atome et une liste
    • nil représente la liste vide
    • () et nil ont le même sens, mais nil est plus souvent utilisé
  • Les éléments d’une liste peuvent être des atomes ou d’autres listes
    • Exemple : (zonk blee strill (croak flonk)) est une liste de quatre éléments, dont le dernier est lui-même une liste de deux éléments
  • Les expressions Lisp elles-mêmes sont des listes
    • (plus 2 2) est aussi une liste
    • L’interpréteur Lisp peut manipuler des listes et des atomes pour créer de nouvelles « commandes » et les évaluer

Valeurs, quote, eval

  • Les atomes peuvent avoir des valeurs
    • Les atomes numériques ont eux-mêmes pour valeur permanente
    • La valeur de nil est nil
    • t est également un atome spécial ayant lui-même pour valeur permanente
  • setq assigne une valeur à un atome
    • (setq pie 4) donne à pie la valeur 4
    • (setq pie (plus 2 2)) donne aussi à pie la valeur 4
  • La valeur d’un atome peut être non seulement un nombre, mais n’importe quel objet Lisp
    • Il peut s’agir d’un atome ou d’une liste
  • quote empêche l’évaluation et permet de traiter une liste ou un atome tel quel comme une donnée
    • (setq pie (plus 2 2)) évalue l’expression interne et stocke 4 dans pie
    • (setq pi '(plus 2 2)) stocke dans pi la liste (plus 2 2) elle-même
  • eval réévalue une expression stockée comme valeur
    • Si la valeur de pi est (plus 2 2), alors (eval pi) renvoie 4
    • L’interpréteur n’évalue normalement qu’un seul niveau, mais eval permet de demander une évaluation supplémentaire

car, cdr, cons

  • Toute liste non nil possède au moins un élément, et le premier élément est appelé car
    • Le car de (eval pi) est eval
    • Le car de (plus 2 2) est plus
  • Le car n’a pas nécessairement à être un nom de fonction
    • Le car de ((1)(2 2) (3 3 3)) est (1)
  • La liste qui reste après suppression du car est le cdr
    • Le cdr de (a b c d) est (b c d)
    • Puis il se réduit à (c d), (d), puis nil
  • nil n’a ni car ni cdr, et tenter d’en prendre le car ou le cdr devrait produire une erreur
  • cons ajoute un nouveau car au début d’une liste existante pour créer une nouvelle liste
    • Quand x vaut (cake cookie), (cons 'pie x) crée (pie cake cookie)
    • La valeur existante de x n’est alors pas modifiée
  • Le résultat change selon la présence ou l’absence de quote
    • Si la valeur de pie est 4, (cons pie x) renvoie (4 cake cookie)
    • (cons 'pie x) ajoute le nom d’atome pie lui-même au début

Différence entre setq et set

  • L’interpréteur Lisp n’évalue pas toujours ses arguments, et setq en est l’exemple représentatif
  • Le premier argument de setq n’est pas évalué : il est traité tel quel comme un nom de variable
    • Le q de setq signifie quote, et le premier argument est traité comme s’il était cité
  • set ressemble à setq, mais évalue aussi son premier argument
    • Quand la valeur de x est k, (set x 7) ne modifie pas x, mais donne à k la valeur 7
  • L’exemple présente la séquence suivante
    • (setq a 'b)
    • (setq b 'c)
    • (setq c 'a)
    • (set a c)
    • (set c b)
  • Après cette exécution, les valeurs de a, b et c sont toutes respectivement a
  • set est rarement utilisé, et ce type de confusion ne se produit pas souvent

Définition de fonctions et lambda

  • La grande force de la programmation tient à la capacité de définir de nouvelles opérations composées à partir d’opérations existantes, puis de répéter ce processus pour accumuler un répertoire d’opérations plus complexes
  • En Lisp, on définit de nouvelles fonctions à partir de fonctions déjà connues
  • rac est un exemple de fonction qui renvoie le dernier élément d’une liste
    • Pour obtenir le dernier élément, il suffit d’inverser la liste puis d’en prendre le car
    • Définition : (def rac (lambda (lyst) (car (reverse lyst))))
  • Le (lyst) qui suit lambda représente le paramètre de la fonction, c’est-à-dire une variable muette
  • Après sa définition avec def, rac devient une fonction utilisable comme car
    • (rac '(your brains)) renvoie brains
  • La fonction readers-digest-condensed-version crée, à partir d’une longue liste, une courte liste ne contenant que le premier et le dernier élément
    • Si l’on traite l’intégralité de Finnegans Wake de James Joyce comme une liste de mots, l’exemple obtient (riverrun the)
  • L’opération inverse, appelée rejoyce, est une fonction imaginaire qui générerait un roman dont le début et la fin sont fixés par deux mots ; son écriture est laissée en exercice au lecteur

Valeurs de retour, effets de bord et style applicatif

  • Certains estiment que deux objectifs sont souhaitables et possibles dans Lisp et les langages apparentés
    • Toutes les expressions renvoient une valeur
    • L’effet d’une expression ne se produit qu’au travers de sa valeur de retour
  • Les dialectes Lisp abordés dans le texte satisfont à la première condition, mais pas nécessairement à la seconde
  • (reverse x) ne modifie pas x lui-même : il crée et renvoie une nouvelle liste identique à x, mais dans l’ordre inverse
    • De la même manière que (plus 2 2) ne modifie pas la valeur de 2
  • cons renvoie lui aussi une nouvelle liste sans modifier la liste existante
  • En revanche, setq est un exemple de commande qui laisse un effet de bord en modifiant une liaison de variable
    • Les effets de bord peuvent aussi inclure les entrées-sorties, en plus de la modification de liaisons de variables
  • Les partisans de la programmation applicative préfèrent que les fonctions calculent et transmettent des valeurs sans effets de bord
  • Dans ce style, les seules liaisons autorisées sont les liaisons lambda, qui apparaissent brièvement pendant un appel de fonction
    • Une fois le calcul de la fonction terminé, les liaisons des variables muettes disparaissent
  • Le narrateur du texte trouve le style applicatif élégant, mais le juge peu pratique pour créer de grands programmes de style IA
  • Dans une programmation applicative stricte, même def devient un cas extrême non autorisé, puisqu’il stocke en mémoire une définition de fonction permanente

Expressions conditionnelles cond et décisions

  • Pour que Lisp puisse faire des choses plus intéressantes, il doit pouvoir prendre des décisions en fonction de ce qui se produit au cours des étapes intermédiaires ; il lui faut alors des expressions conditionnelles
  • Exemple : (cond ((eq x 1) 'land) ((eq x 2) 'sea))
    • Si x vaut 1 : land
    • Si x vaut 2 : sea
    • Sinon, renvoie nil
  • eq est une fonction Lisp qui renvoie t si les valeurs de ses deux arguments sont identiques, et nil sinon
  • Une expression cond commence par le nom de fonction cond et comporte plusieurs clauses
    • Chaque clause est une liste de deux éléments composée d’une condition et d’un résultat
    • Les conditions sont testées dans l’ordre ; dès qu’une condition renvoie une valeur non nil, le résultat correspondant est évalué et renvoyé comme valeur de l’ensemble du cond
    • Les clauses suivantes ne sont pas examinées
  • En plaçant à la fin une clause fourre-tout dont la condition est t, on peut renvoyer un résultat par défaut au lieu de nil
    • Exemple : un cond renvoyant air si les conditions land et sea échouent toutes les deux est présenté

Puissances et structure récursive

  • À la fin du texte apparaissent des définitions de fonctions au motif très net
    • square vaut k * k
    • cube vaut k * square(k)
    • 4th-power vaut k * cube(k)
    • Le même schéma continue ainsi
  • La question posée est de savoir s’il est possible de définir une fonction à deux paramètres power qui englobe d’un seul coup tout ce motif
    • (power 9 3) doit donner 729
    • (power 7 4) doit renvoyer 2 401
  • Tous les outils nécessaires ont été présentés dans le texte, et il est fait appel à l’ingéniosité du lecteur
  • L’histoire finale du porpuquine glazunkien traite la structure récursive comme une fable
    • Les piquants du porpuquine sont des porpuquines plus petits
    • En Glazunkia extérieure, il y a toujours 9 piquants ; en Glazunkia intérieure, toujours 7
    • Un porpuquine de 0 pouce n’a pas de piquants, ce qui arrête la régression infinie
  • Le « buying power », ou « power », de cet animal est lié au nombre de nez de petits porpuquines de 0 pouce qu’il contient, et le texte se conclut sur cette métaphore reliée au problème power présenté plus haut

Contexte de republication du texte original

  • Le début du Gist indique qu’il s’agit d’une introduction à Lisp de Douglas Hofstadter, découverte dans un ancien numéro de Scientific American du milieu des années 1980
  • Les exemples peuvent encore être exécutés dans Emacs en installant quelques alias
    • plus+
    • quotient/
    • times*
    • difference-
  • La fin indique que, si l’on a apprécié la présentation singulière de Lisp par Hofstadter, on peut trouver d’autres textes similaires dans son livre Metamagical Themas

1 commentaires

 
GN⁺ 2024-10-18
Avis sur Hacker News
  • Les noms de fonctions "oval" et "snot" dans l’exemple m’ont embrouillé, puis au bout de quelques secondes j’ai compris qu’il s’agissait respectivement de coquilles pour "eval" et "snoc"
    Le code original devrait se lire comme (cond ((eq (eval pi) pie) (eval (snoc pie pi))) (t (eval (snoc (rac pi) pi))))
    En récupérant l’article original de Scientific American sur https://www.jstor.org/stable/24968822 pour vérifier, il s’est avéré que oval/snot étaient bien des coquilles pour eval/snoc

    • Correction de la correction : "oval" et "snot" sont bien des coquilles pour "eval" et "snoc"
      Et snoc semble être cons à l’envers, rac étant car à l’envers
    • Il pourrait aussi s’agir d’une erreur d’OCR
  • Cet article montre une fois de plus que l’une des raisons essentielles de la faible popularité de Lisp tenait à la manière dont on expliquait Lisp
    Ce genre d’articles sur Lisp ne m’a rien appris, quand j’étais enfant, qui soit réellement utilisable, et ne montrait pas non plus à quel point une tâche X devenait plus facile par rapport à l’assembleur/C/Pascal, etc.
    Aujourd’hui, j’aurais montré quelque chose comme : « Un correcteur orthographique qui a pris 7 mois en assembleur ? Sur un micro-ordinateur utilisant de la mémoire commutée par banque, c’est presque trivial en Lisp, et même sur un CPU médiocre on peut rendre le garbage collection déterministe »
    Tant d’articles et de manuels sur Lisp répétaient seulement listes, récursion et IA, sans montrer comment faire des choses utiles, que j’ai l’impression d’avoir perdu du temps à programmer comme avec une pince à épiler, des grains de riz et de la colle

    • Common Lisp: A Gentle Introduction to Symbolic Computation peut être utile dans ce contexte : https://www.cs.cmu.edu/~dst/LispBook/
    • Il y a aussi Practical Common Lisp : https://gigamonkeys.com/book/
    • Les articles suivants de Hofstadter contenaient des exemples plus intéressants
      Le premier magazine Byte que j’ai vu enfant contenait du code Lisp pour de la dérivation symbolique et de la simplification algébrique ; c’était difficile à suivre, mais il y avait quelque chose de séduisant
      Ça n’aurait certainement pas été plus simple en Basic, et j’ai découvert plus tard que ce code n’était pas si excellent que ça, mais ce n’est qu’après avoir rencontré XLisp sur PC à la fin des années 80, puis SICP, que je suis vraiment tombé dans Lisp
  • J’aime vraiment l’écriture de Hofstadter. Il saisit bien l’émotion que j’ai ressentie en découvrant Lisp
    J’étais un gamin qui apprenait à programmer dans les années 80 et, au lycée puis au début de l’université, j’avais déjà un peu touché à BASIC, Fortran, Pascal et COBOL ; malgré leurs différences, ils avaient fondamentalement des points communs
    Mais le premier cours d’informatique à UC Berkeley se faisait avec Scheme, un dialecte de Lisp, et ça a été un choc total
    Comme le dit Hofstadter, c’était ce qui se rapprochait le plus des mathématiques, cela me rappelait beaucoup les cours de théorie mathématique, et c’était le premier beau langage que je découvrais
    J’ai particulièrement aimé cette citation : « Lisp et Algol sont bâtis autour d’un noyau naturel, comme une branche des mathématiques. Au cœur de Lisp se trouve une pureté cristalline qui ne fait pas seulement appel au sens esthétique, mais le rend aussi bien plus flexible que la plupart des langages »

    • Je me demande si tu as essayé Haskell. Pour moi, comme il est fait de définitions plutôt que de procédures, il paraît beaucoup plus proche des mathématiques, et son apparence même ressemble aux mathématiques
    • Au risque de m’éloigner un peu du billet original, je pense que l’appeler « mathématiques » peut au contraire brouiller les choses
      Même du point de vue de quelqu’un qui voulait devenir topologue algébrique, Scheme ressemble plutôt à une expression élégante et minimale d’un style de programmation répandu dans l’ensemble des langages à typage dynamique et à garbage collection
      Il paraît complet, et en ce sens ressemble à une « théorie » : on peut réfléchir à une manière de résoudre un problème en Scheme, puis la transposer dans un autre langage à typage dynamique et aboutir à une solution élégante
      Scheme était un langage qui simplifiait et clarifiait le Lisp traditionnel de l’époque, notamment avec la portée lexicale et un espace de noms unique pour les fonctions et les variables
  • En 1983, environ vingt ans après les débuts du domaine, on parle déjà d’IA visant à programmer des ordinateurs dotés de « flexibilité, bon sens, intuition, créativité, conscience de soi, humour »
    C’est assez amusant, parce que cette liste se lit comme celle de tout ce que les LLM ne savent vraiment pas faire
    Malgré tout, on peut dire qu’il y a eu au moins un progrès non nul dans cette direction

    • Comparés aux gens ordinaires que je rencontre, les LLM semblent montrer du bon sens, de l’intuition, de la créativité, de la conscience de soi et de l’humour au niveau d’une personne moyenne
      Peut-être même davantage, même si le dire ainsi me paraît un peu étrange à moi-même
    • Le groupe de recherche de Hofstadter a une longue histoire de travail sur ce type de problèmes
      Même si beaucoup de choses n’ont pas encore débouché sur des résultats, cela reste intéressant à lire
  • Les deux articles compagnons mentionnés avec celui-ci figurent dans les chapitres 17 à 19 du livre de Hofstadter Metamagical Themas, avec d’autres articles de la même chronique de Scientific American
    [0]: https://www.goodreads.com/book/show/181239.Metamagical_Thema...
    Le titre du livre vient du titre de la chronique, une anagramme de « Mathematical Games », que Martin Gardner tenait dans Scientific American, et dont Hofstadter a pris la suite

    • J’adore vraiment ce livre. Je le recommande vivement à celles et ceux qui aiment le classique de Hofstadter, Gödel, Escher, Bach
    • Ce livre est vraiment magnifique à lire comme à regarder, avec beaucoup d’illustrations intéressantes
  • L’article affirme qu’appliquer car ou cdr à nil devrait produire une erreur, comme une division par zéro, mais ce n’est souvent plus le cas dans les Lisp modernes.
    Dans le Lisp original défini par John McCarthy, CAR et CDR n’étaient pas définis pour NIL : <https://dl.acm.org/doi/pdf/10.1145/367177.367199>
    Mais Common Lisp et Emacs Lisp définissent (car nil) et (cdr nil) comme valant nil : <https://www.lispworks.com/documentation/HyperSpec/Body/f_car...>, <https://www.gnu.org/software/emacs/manual/html_node/elisp/Li...>

    • Je me suis demandé ce qu’il en était dans Maclisp, alors je me suis connecté en telnet à l’ITS public de Lars Brinkhoff pour vérifier.
      Dans LISP 2156, (status lispversion) renvoyait /2156, et (car nil) comme (cdr nil) renvoyaient tous deux NIL.
    • Il est étonnant que des détails d’implémentation aussi concrets que car et cdr aient joué un rôle central dans la naissance d’un langage conçu mathématiquement.
      Les opérateurs les plus fondamentaux et les plus célèbres de « List Processor » n’ont pas été conçus pour agir sur des listes, mais sur cons, un élément particulier de la représentation machine utilisée par Lisp pour construire des structures de données.
      Un cons n’est pas non plus toujours interprété comme une liste, et la liste, pourtant cruciale comme cas de base des fonctions récursives sur les listes, n’est même pas représentée par un cons.
      Soixante ans plus tard, la plupart des programmes Lisp restent remplis d’opérations sur les cons, et un nom plus exact aurait peut-être été « Cons Processor ».
      Cela rappelle que Lisp est né à une époque où langage et implémentation devaient s’emboîter étroitement, ce qui rend d’autant plus impressionnante la réussite consistant à fonder un langage informatique sur la logique mathématique.
    • Scheme, lui, ne fonctionne pas ainsi. Prendre le CAR ou le CDR de nil est une erreur.
    • Malheureusement, dans Scheme, le car/cdr de nil est une erreur, ce qui rend le code assez peu ergonomique pour un débutant comme moi.
      Je préfère tout de même Guile Scheme à Common Lisp, donc c’est dommage.
    • Je me demande s’il existe un terme pour désigner ce choix de conception où appliquer quelque chose à nil ne produit pas d’erreur et renvoie simplement nil.
      SQL me vient aussi à l’esprit, et comme je considère personnellement que c’est un mauvais choix, à défaut d’autre terme je serais tenté d’appeler cela « bleeding nils/NULLs ».
      C’est encore pire si nil n’est pas équivalent à false dans les comparaisons booléennes.
      En Ruby et en Elixir, nil est traité comme false, et Elixir fournit séparément and, qui n’accepte que de vrais booléens, et &&, qui traite nil comme false.
      Ce type de conception peut rendre le code plus propre au début, mais un nil mal géré qui est en réalité une erreur peut finir par se manifester dans une partie totalement différente du programme, plusieurs piles d’appels plus loin, ce qui rend le débogage bien plus difficile.
  • Je connaissais déjà les informations sur Lisp dans cet article, mais je l’ai quand même lu avec plaisir. Hofstadter a vraiment un style savoureux.
    J’ai particulièrement aimé sa plaisanterie consistant à inventer rejoyce, l’opération inverse de readers-digest-condensed-version : en exécutant (rejoyce 'Stately 'Yes), une fée Lisp générerait depuis le début l’intégralité d’Ulysses telle que James Joyce aurait pu l’écrire.
    Cela a pris un certain temps, mais nous y sommes finalement arrivés ; même si l’IA de 2024 n’est pas exactement celle qu’il imaginait en 1983, reproduire du texte à partir d’une courte amorce est aujourd’hui une tâche qui correspond assez bien à l’IA.

  • Je pense que Lisp reste l’un des principaux langages capables d’exprimer le concept de boucle étrange abordé par Hofstadter
    Lisp n’est pas le seul langage homoïconique, mais parmi les langages que les gens savent réellement utiliser, il fait partie des plus importants dont eval ne reçoit pas une chaîne à parser
    Je n’aime pas que les gens assimilent Lisp à l’ensemble de la programmation fonctionnelle
    Je n’ai rien contre la programmation fonctionnelle, mais le caractère symbolique de Lisp est bien plus intéressant, et je trouve infiniment amusant qu’on puisse aussi très facilement écrire du code piloté par des sections (go tag), à la manière d’une programmation avec GOTO

    • Un autre langage homoïconique, réellement fonctionnel, qui a brièvement connu une adoption grand public dans les années 2000, est XSLT
      Avec XSLT et XML, des fonctionnalités de métaprogrammation consistant à générer du XSLT au lieu d’écrire directement du code répétitif ont été assez largement utilisées
      En revanche, la syntaxe était un problème encore plus important que celle de Lisp
      Il n’est pas facile de créer un langage dont la syntaxe soit agréable au quotidien tout en restant maniable comme arbre de syntaxe abstraite, et Lisp est l’un des rares exemples relativement réussis
    • En particulier, dès qu’on va jusqu’à la programmation fondée sur les continuations en Scheme, la frontière entre programmation fonctionnelle et non fonctionnelle devient presque dénuée de sens
    • Je me demande ce que le programmeur de 2024 tire réellement de Lisp, en matière de support homoïconique/symbolique profond, que functools de Python ne permet pas vraiment : https://docs.python.org/3/library/functools.html
      Je construis une AGI symbolique sans Lisp, et j’aimerais avoir des pistes de la part de spécialistes
      Ce que j’ai compris des fonctionnalités côté Python, ce sont des fonctions comme filter(), map() et reduce() qui appliquent d’autres fonctions à des objets itérables, des wrappers comme @singledispatch qui routent les appels, des fonctions comme @cache et partial() qui offrent du contrôle de flux ou des commodités de performance, et des mécanismes comme wraps() qui permettent d’envelopper arbitrairement des fonctions
      La plupart de ces éléments me semblent surtout être des commodités pour appeler des fonctions de façon particulière, et ne pas atteindre le niveau des éloges faits à Lisp, où « l’introspection » est traitée comme une préoccupation de premier ordre
      J’aimerais savoir ce que Lisp apporte concrètement qui manque à ces fonctionnalités
    • C’est aussi parce que beaucoup de gens ne rencontrent Lisp que par le monde académique, et que les universitaires qui enseignent Lisp ne s’intéressent pas vraiment à développer quelque chose avec Lisp
      Ils utilisent Lisp comme moyen de transmettre des concepts, et ces concepts tournent généralement autour de la récursion fonctionnelle
      Scheme et la culture qui l’entoure y sont aussi pour quelque chose
      Scheme n’est pas uniquement un langage fonctionnel, mais il met davantage l’accent sur la programmation pure que les familles Lisp précédentes ; son langage de base fournit des structures de récursion terminale plutôt que des structures d’itération, et il exige des implémentations l’optimisation des appels terminaux
  • Même en Common Lisp, si l’on définit defalias comme une macro, on peut l’utiliser comme le defalias d’Emacs : https://stackoverflow.com/questions/24252539/defining-aliase...

  • J’avais aimé l’article de Hofstadter sur Lisp paru dans Metamagical Themas, et j’ai adapté et mis au propre le code du dernier article de cette série en Clojure pour un groupe d’étude au travail
    [1] http://johnj.com/posts/oodles/

    • Je me demande aussi s’il existe une version traduite vers un Lisp moderne