Ces derniers temps je me suis heurté aux limitations de Python (le language et la "plateforme") et je suis en passe de trouver les solutions dans Common Lisp. Vous allez voir, les possibilités sont accrues. Néanmoins, ce n'est pas sans quelques frustrations et je ne recommande rien encore: je n'ai pas encore pu tout tester, et la documentation doit s'améliorer (heureusement, c'est en cours).

D'abord quelques réponses aux premières objections et quelques bons liens:

  • vous pouvez utiliser Emacs, Vim, Atom (atom-slime) ou LispWorks (éditeur propriétaire),
  • qui fait aujourd'hui du CL ? (en effet, pas grand monde ne blogue à ce propos):
    • déjà, un éventail de softs à succès dans l'industrie: http://lisp-lang.org/success/
    • c'est un language utilisé dans l'IA, à ce titre enseigné dans des universités
    • le métro londonien et d'autres capitales (pdf)
    • pgloader (ré-écrit de Python en CL pour, entre autres, un gain de performances d'un facteur 30 -je répète, trente ;)),
    • Framasoft pour les Framanotes (Turtl, backend en CL et front en JS),
    • la recherche en bio-informatique (Clasp, LLVM et C++)
    • les sponsors de Quicklisp (le gestionnaire de paquets): Rigetti, "on a mission to build the world's most powerful computer",
    • les sponsors de la conférence ELS,
    • l'entreprise FranzInc qui vend son implémentation AllegroCL et des produits en CL, notamment Allegrograph, sa solution de graphs sémantiques,

Les bons liens pour débuter:

Ok, donc, n'êtes-vous pas frustré-es comme moi, en Python, par les choses suivantes:

  • manque de paradigme fonctionnel (malgré Coconut et autres, qui ne comblent pas les limitations suivantes),
  • Python 2 VS Python 3,
  • manque de typage, malgré my-py, encore une surcouche,
  • Python est lent, on s'en rend compte même pour des choses peu complexes,
  • développement pas idéal pour moi:
    • j'en ai marre qu'au moindre changement de source tout le serveur de dév doive redémarrer,
    • je peux difficilement lancer un test unitaire précis (sauf par un long chemin manuel sur la ligne de commande ou une config très personnelle d'Emacs),
    • j'en ai marre de devoir redémarrer toute mon appli pour lancer un ou des tests,
    • et ainsi de perdre mes essais dans ipython,
    • de même de devoir tout redémarrer pour prendre en compte les changements de code dans ipython,
  • quasi-impossibilité de fournir un exécutable pour une application, sauf peut être pour Windows, et encore moins pour des applis web. J'ai essayé Nuitka, Pyinstaller, Electron pour Python… tous des hacks inaboutis qui demandent l'inclusion manuelle de plein de fichiers, et impossible de livrer des webapps (oh, peut être Briefcase ?).
  • les applis webs justement: j'aimerais avoir un et un seul language pour toute mon appli… et voir si ça marche bien (sinon, back en CL et front en JS (ou ClojureScript) est évidemment possible, certains font ça actuellement). Aujourd'hui on doit forcément passer par un framework JS, du html (ou pugjs) et un language de template, ce qui fait de nouvelles choses à apprendre, à configurer, à débugguer, et au final donne un truc moche et illisible: les templates jinja d'une vraie appli sont plein de tags spéciaux, de factorisation du html avec les macros jinja, d'héritage pugjs, de tags pour laisser la place à Angularjs ou Vue.js,… Nagare ?
  • déploiement de projets web: encore une nouvelle gageure pour laquelle il n'existe pas de manière simple et "built-in": on doit se fabricoter des règles Fabric, Ansible, configurer les superviseurs, serveurs web,…

    Or oui, Common Lisp résoud (apparemment) tout cela.

    Mais pas sans contreparties: documentation pourrie (sauf dans les livres, ce qui n'est pas rien, et en amélioration sur quelques sites rares), une entrée en matière difficile et frustrante (moins maintenant avec le Cookbook, espérons) et certains projets encore trop confidentiels.

1 Paradigme fonctionnel

Common Lisp est multi-paradigme, ce qui nous donne des macros loop pas très "lispy", mais on peut quand même faire du fonctionnel avec des map, closures, pattern matching (librairie), pipes (threading macro, librairie), …

Ce qui était frustrant pour moi, débutant impatient, est le manque de généricité, c'est à dire le nombre de fonctions différentes qui réalisent pourtant la "même" action mais sur un type différent: en python les opérateurs marchent sur toute sorte de structures de données (+, len, l'accès [],…), mais en CL ce sera des fonctions différentes… en effet, le système objet (CLOS, CL Object System) a été développé après coup. C'est donc possible (méthodes génériques) mais peu usité. La librairie cl21, "CL pour le 21e siècle", redéfinit pas mal de fonctions de manière générique. CL21 ne fait pas l'unanimité, forcément, mais marche très bien pour moi :)

2 Typage

CL n'est ni typé statiquement ni totalement dynamique comme Python. Il calcule bien plus d'inférences de type et nous décèle plus d'erreurs et de warnings à la compilation que Python (… forcément ?).

Tous les détails sont dans (eheh surprise): le papier de recherche du compilateur Python pour Common Lisp, développé à l'Université Carnegie Mellon en 1992: https://www.researchgate.net/publication/221252239_Python_compiler_for_CMU_common_Lisp où ici "Python" n'a rien à voir avec notre language, c'est le nom de ce compilateur :)

En pratique, on obtient ces erreurs et warnings à chaque fois qu'on compile une fonction, c'est à dire souvent (dans Slime, c'est le réflexe C-c C-c).

On peut également désassemble le code d'une fonction pour vérifier qu'on l'a optimisée et de la bonne manière.

Je vous renvoie vers http://blog.30dor.com/2014/03/21/performance-and-types-in-lisp/ et au livre "Common Lisp Recipes" (paru en 2015, étonnant mais vrai ;) Jetez un œil ici puis achetez-le !).

Petit exemple. Si on a:

(defun foo (x y)
  (declare (type integer x y))
  (logxor x y))

foo est reconnu comme une fonction qui prend 2 integers en argument:

(describe 'foo) ⋮ Derived type: (FUNCTION (INTEGER INTEGER) (VALUES INTEGER &OPTIONAL))

Mais un integer c'est assez général: ('scusez je vous commente pas l'assembleur, c'est acquis)

(disassemble 'foo)
; disassembly for FOO
; 02B8BC8F: 4883EC18 SUB RSP, 24
; no-arg-parsing entry point
; 93: 48896C2408 MOV [RSP+8], RBP
; 98: 488D6C2408 LEA RBP, [RSP+8]
; 9D: B904000000 MOV ECX, 4
; A2: 488B0425980B1020 MOV RAX, [#x20100B98]
; AA: FFD0 CALL RAX
; AC: 488BE5 MOV RSP, RBP
; AF: F8 CLC
; B0: 5D POP RBP
; B1: C3 RET

On déclare un integer non signé de 32 bits:

(defun foo (x y)
  (declare (type (unsigned-byte 32) x y))
  (logxor x y)) FOO

(describe 'foo)
[…]
; Derived type: (FUNCTION ((UNSIGNED-BYTE 32) (UNSIGNED-BYTE 32))
;                (VALUES (UNSIGNED-BYTE 32) &OPTIONAL))

et l'assembleur:

(disassemble 'foo)
; disassembly for FOO
; 02C9EB0F:     4831FA           XOR RDX, RDI ; no-arg-parsing entry point
;       12:     488BE5           MOV RSP, RBP
;       15:     F8               CLC
;       16:     5D               POP RBP
;       17:     C3               RET

Ce qui va s'exécuter bien plus vite.

Bonus:

3 Performance

Le CL compile en code machine. Avec quelques déclarations de type on peut obtenir du code aussi ou plus performant que du code C.

4 Environnement de développement

J'utilise Emacs et Slime (il existe Sly et des plugins pour Vim et Atom) et le développement est très interactif. Dans le code source, on peut recompiler une fonction à la fois, lancer un test unitaire à la fois, travailler dans le REPL, recompiler une fonction, l'utiliser de suite, sans perdre les variables et données du REPL, enregistrer l'état courant de l'image (afin par exemple de garder des variables qui auraient pris du temps à être calculées), on tombe dans un débuggueur interactif lors d'une exception, on peut (il paraît) y modifier des variables ou fonctions et reprendre l'exécution d'où elle s'est arrêtée (système de conditions et restarts), on peut inspecter la structure de n'importe quoi au clavier ou à la souris, on débuggue un programme avec trace, step et cie, on débuggue une instance à distance, y compris si elle est dans l'espace,…

Inspecter la source d'un symbole n'a jamais été aussi facile (M-. sous Slime), sachant que je ne me suis jamais installé ça sous Emacs, à la fois par manque de volonté, d'informations et de facilité, alors que je développe des extensions en Elisp… De même, inclure dans son environnement la version en développement d'une librairie est super facile, et donc de contribuer à des trucs dont on n'aurait pas eu l'idée en Python, car il suffit de la mettre dans ~/quicklisp/local-projects/ et elle est chargée en priorité par Quicklisp (mais on peut aussi utiliser l'équivalent des virtualenvs avec Qlot). D'ailleurs, ce gestionnaire de paquets est encore différent de ce qu'on connaît par ailleurs, notamment par le fait qu'il se charge de tester que toutes les librairies disponibles ne rentrent pas en conflit entre elles.

C'est un environnement différent, qu'il faut se laisser le temps d'apprivoiser.

5 Fournir un exécutable ou une image

C'est une fonctionnalité de base des implémentations CL. J'ai très facilement essayé ça, c'est une commande à lancer, même pour des applis web (qui contiennent leur serveur web).

Un exécutable standalone pèsera quelques Mo. On peut aussi partager des scripts avec Roswell.

De plus, ECL (Embeddable Common Lisp) compile en C et peut fournir un simple exécutable, pour toutes plateformes, y compris Android. ABCL est pour la JVM, et il y en a d'autres.

6 Développement web

Le serveur par défaut est Hunchentoot (un mix de serveur et de framework), Clack est une abstraction (WSGI chez Python) pour pouvoir changer de serveur (Hunchentoot, Woo (basé sur libev, de Nodejs), Wookie (asynchrone),…).

Caveman et Lucerne sont des frameworks avec une approche traditionnelle.

On peut utiliser des templates à la Django avec Djula ou du lisp avec Spinneret, cl-who ou d'autres.

Parenscript est LE compilateur de CL vers Javascript.

Ansi, on peut très bien développer un backend en CL et le front en ce qu'on veut. C'est l'approche de Turtl, de Potato (similaire à Slack, front en ClojureScript), de pgchart (oh, c'est une appli web "standalone"),…

Est-ce que tout ce monde marche ensemble ? En partie oui, dans le vénérable Weblocks. Son site actuel est une honte, mais je le mentionne car il est toujours maintenu et un gars est en train de le revitaliser dans sa branche de développement. Il a créé une nouvelle doc sur un nouveau site (http://40ants.com/weblocks/) et s'est lancé depuis quelques mois dans une refonte du code. Son tutoriel (le fameux todomvc) marche, et est facile et parfaitement lisible.

Enfin, avec un déploiement facile, ce pourrait être la panacée.

7 Déploiement

On a déjà la création d'exécutables "standalones" et d'images.

On a des outils pour faciliter encore le processus (deploy). Certains frameworks permettent d'héberger plusieurs sites web dans une image (Weblocks, Radiance, Hunchentoot ?). Weblocks ou des librairies gèrent les dépendances JS pour nous.

On peut se connecter à une image qui tourne à distance.

Le site de Caveman donne un moyen pour du déploiement "à chaud" (hot swaping, déploiement sans interruption de service).

Ceramic nous permet de mettre des web apps sur Electron.

J'ai déjà eu besoin de pouvoir faire ça en Python. Ça existe en CL, et je me demande bien s'il existe une plateforme aussi complète.

8 En guise de conclusion

Je soutiens que la plaie de l'écosystème CL est son manque de documentation, de documentation attractive et plus globalement de communication. Heureusement, ça bouge de ce côté-là (lisp-lang.org, le Cookbook, refonte de la doc de référence,…).

Le language en lui-même contient des inconsistances et trucs un peu vieillots, mais ce peut être corrigé par des librairies (cl21 ou de plus spécialisées moins intrusives) (et… on s'habitue). Mais étant donné toutes les fonctionnalités du language et de la plateforme, il nous paraît carrément valoir le coup de ne pas se précipiter et de nous familiariser avec ce monde, un peu alien.

On reviendra, peut être, pour comparer plus spécifiquement les écosystèmes CL et Python. En attendant:

sudo apt-get install sbcl

ou voyez Portacle, environnement de développement portable et multiplateforme (Emacs + SBCL + Quicklisp + Slime + Git).