Aller au menu - Aller au contenu

Voici le blog de : acieroid

py-t2n – Talk To NXT with Python

UP | HOME

py-t2n – Talk To NXT with Python

py-t2n is an utility to get informations, send files or receives files from a NXT Lego Brick, through USB or Bluetooth.

The usage is the same as the one of t2n, and the output is mostly the same. The aim of py-t2n is to have a simple tool like t2n, but which is alive and compatible with last NXT firmwares (t2n doesn't work with firmwares versions > 1.05). This goal is achieved easily thanks to the nxt-python library (some of the code is actually taken from nxt-python's scripts). py-t2n is GPLv3 licensed.

Table of Contents

  • 1 Installation
  • 2 Usage

1 Installation

Installation is easy:

git clone git://github.com/acieroid/py-t2n
cd py-t2n
python setup.py install

py-t2n has been tested successfully on FreeBSD and Linux, and should work on Microsoft Windows and any other OS where nxt-python is running.

2 Usage

usage: t2n.py [-h] [-b] [-i] [-ls] [-put <file>] [-get <file>] [-version]

Talk to NXT with Python

optional arguments:
  -h, --help   show this help message and exit
  -b           Check battery level
  -i           Print NXT info
  -ls          List files
  -put <file>  Upload file
  -get <file>  Download file
  -version     Print this software's version

Author: Quentin Stievenart <>

Date: 2011-03-09 17:14:42 CET

HTML generated by org-mode 6.33x in emacs 23

Utilisez les bons outils pour programmer en Common Lisp

UP | HOME

Utilisez les bons outils pour programmer en Common Lisp

Table of Contents

  • 1 Introduction
  • 2 Emacs
    • 2.1 Configuration
    • 2.2 Note: installation d'un mode Emacs
    • 2.3 Plus d'informations
  • 3 Éditeur externe
  • 4 SLIME
    • 4.1 Installation & Configuration
    • 4.2 Utilisation
  • 5 Paredit
    • 5.1 Installation & Configuration
    • 5.2 Utilisation
      • 5.2.1 Les commandes d'insertion
      • 5.2.2 Commandes de suppression
      • 5.2.3 Commandes de changement de profondeur
      • 5.2.4 Barfage et Slurpage
  • 6 La documentation
    • 6.1 Sites internet
    • 6.2 Fonctions standards
      • 6.2.1 describe
      • 6.2.2 documentation
      • 6.2.3 apropos
      • 6.2.4 inspect
    • 6.3 SLIME
      • 6.3.1 SLIME inspector
  • 7 Les outils de débuggage
    • 7.1 trace
    • 7.2 Invoquer le debugger
    • 7.3 Single stepping
    • 7.4 Les conditions et les restarts
    • 7.5 SLIME
  • 8 Bibliothèques
    • 8.1 Installation de bibliothèques
      • 8.1.1 Quicklisp
      • 8.1.2 Autres bibliothèques
  • 9 Les livres & articles
    • 9.1 Apprentissage du Common Lisp
    • 9.2 Livres plus avancés
    • 9.3 Autres
  • 10 Conclusion

1 Introduction

Dans cet article nous allons voir plusieurs outils permettant de développer vos applications en Common Lisp plus efficacement. Ces outils vont d'un bon éditeur avec une bonne configuration à de bons moyens de débuggage, tout en passant par une connaissance de la documentation existante et l'utilisation de bibliothèques appropriées.

2 Emacs

Le Common Lisp ayant un modèle de développement itératif et interactif, il vous faudra un éditeur qui facilite l'interactivité. C'est le cas de GNU Emacs, l'éditeur généralement préféré par les programmeurs Lisp. Ce n'est évidemment pas le seul éditeur adapté au Lisp, citons par exemple Vim avec les plugin slimv ou Limp, ou bien l'IDE non libre de Lispworks. Néanmoins, slimv et Limp ne font qu'imiter ce qu'Emacs peut faire avec SLIME, et Lispworks n'étant pas libre, Emacs reste préférable.

Pour installer Emacs, référez vous à la documentation de votre distribution.

2.1 Configuration

Emacs est un éditeur configurable au souhait en Emacs Lisp. Je ne vais pas parler ici de la configuration générale d'Emacs, mais plutôt de la configuration relative au Lisp. Pour plus d'information sur la configuration d'Emacs, référez vous à la documentation d'Emacs.

Le lisp étant essentiellement composé de parenthèses, il est assez pratique de pouvoir naviguer entre ces parenthèses et reconnaître facilement quelle parenthèse ouvrante correspond à quelle parenthèse fermante.

Pour la navigation entre parenthèses, on peut regretter de ne pas avoir de commande équivalente au % de Vim, qui permet de passer d'une parenthèse ouvrante à la parenthèse fermante correspondante, et inversément. Néanmoins on trouve sur l'Emacs Wiki une page consacrée à cela. Ainsi, pour avoir C-% qui se comporte comme le % de Vim, il suffit d'ajouter ceci dans votre .emacs:

(defun goto-match-paren (arg)
  (interactive "p")
  (cond ((looking-at "\\s\(") (forward-list 1) (backward-char 1))
        ((looking-at "\\s\)") (forward-char 1)
         (backward-list 1))
        (t (self-insert-command (or arg 1)))))

(global-set-key (kbd "C-%") 'goto-match-paren)

Pour une navigation et une manipulation des parenthèses encore plus efficaces, il y a Paredit, que nous verrons en détail par après.

Au niveau de l'apparence des parenthèses, il est important que l'on puisse savoir facilement quelle parenthèse ouvrante correspond à quelle parenthèse fermante. Pour cela, il y a deux façons de faire, qui peuvent êtres combinées:

  • Mettre en avant la parenthèse correspondante à celle où on se trouve, pour cela il y a le show-paren-mode qui est fourni avec Emacs:
    (require 'paren)
    (show-paren-mode t)
    (setq blink-matching-paren nil)
    
  • Avoir une couleur correspondante à chaque paire de parenthèses, c'est ce que fait le rainbow-delimiters-mode (voir la note qui suit si vous ne savez pas comment l'installer):
    (require 'rainbow-delimiters)
    (rainbow-delimiters-mode t)
    (add-hook 'slime-mode-hook (lambda () (rainbow-delimiters-mode t)))
    

2.2 Note: installation d'un mode Emacs

Pour installer un mode, il faut récupérer le .el correspondant et le placer par exemple dans ~/.emacs.d. Soyez sûr d'avoir la ligne suivante dans votre .emacs:

(add-to-list 'load-path "~/.emacs.d/")

Il vous suffit alors d'utiliser require ou autoload pour charger le mode:

(require 'nom-du-mode)

2.3 Plus d'informations

Bien évidemment, ce qui a été présenté dans cette partie ne concerne que les modes que j'utilise personellement. Il existe énormément d'autre modes qui peuvent être utiles qui ne sont pas présentés ici. Pour plus d'informations, référez vous au Emacs Wiki et plus particulièrement à la catégorie sur les parenthèses.

3 Éditeur externe

Néanmoins, si vous êtes plus confortable avec un éditeur autre qu'Emacs, ou que pour quelconque raison vous ne voulez pas utiliser Emacs, vous allez passer beaucoup de temps dans le REPL de votre Lisp, il vaut donc mieux bien le configurer.

Le REPL de CLISP est assez bon de base (support de type readline, autocomplétion etc.).

Pour ce qui est de SBCL, il n'y a pas de telles fonctionnalitées par défaut. Il faut donc:

  • soit utiliser rlwrap pour avoir un support de readline
  • soit installer linedit qui apporte des fonctionnalitées similaires à celles du REPL de CLISP (linedit est installable via Quicklisp, voir plus loin).

Si vous installez linedit, veillez à ajouter ceci à votre ~/.sbclrc:

;;; Check for --no-linedit command-line option.
(if (member "--no-linedit" sb-ext:*posix-argv* :test 'equal)
    (setf sb-ext:*posix-argv* 
          (remove "--no-linedit" sb-ext:*posix-argv* :test 'equal))
    (when (interactive-stream-p *terminal-io*)
      (require :sb-aclrepl)
      (require :linedit)
      (funcall (intern "INSTALL-REPL" :linedit) :wrap-current t)))

4 SLIME

SLIME, le Superior Lisp Interaction Mode for Emacs est le mode ultime pour programmer en Common Lisp. Il permet d'avoir un buffer connecté à votre processus Lisp, et d'y envoyer du code à évaluer depuis un fichier Lisp avec un simple raccourci. Il inclut également de nombreuses features très pratique aussi liées à la documentation, l'indentation, la recherche de défitions, le débuggage etc.

4.1 Installation & Configuration

Il est possible que SLIME soit fourni avec votre distribution. Si ce n'est pas le cas, récupérez la version depuis le CVS:

cd ~/.emacs.d/
cvs -d :pserver:anonymous:anonymous@common-lisp.net:/project/slime/cvsroot co slime

Et ajoutez le dossier à votre .emacs:

(add-to-list 'load-path "~/.emacs.d/slime")

Et pour charger SLIME:

(require 'slime-autoloads)

Ensuite, SLIME possède de nombreuses options de configurations.

Tout d'abord, vous pouvez indiquer quelle implémentation de Common Lisp vous souhaitez utiliser, et éventuellement vous pouvez lister des implémentations supplémentaires:

(setq inferior-lisp-program "sbcl")
(setq slime-lisp-implementations
      `((sbcl ("sbcl"))
        (clisp ("clisp"))
        (ecl ("ecl"))))

Il faudra ensuite appeler slime-setup avec la liste des packages supplémentaires que vous souhaitez charger (vous pouvez obtenir la liste de ces packages dans la documentation de SLIME). Par exemple:

(slime-setup '(slime-repl slime-c-p-c slime-editing-commands slime-asdf slime-scratch))

Pour charger SLIME automatiquement à la visite d'un fichier .lisp, on peut ajouter le hook suivant:

(add-hook 'slime-mode-hook
          (lambda ()
            (unless (slime-connected-p)
              (save-excursion (slime)))))

4.2 Utilisation

Je ne vais pas ici présenter tout ce qui est faisable avec SLIME mais simplement donner les raccourcis les plus utiles. Pour toute information complémentaire, regardez du côté de la documentation de SLIME.

Une session Lisp se passe généralement comme suit:

  • Vous entrez quelques lignes de codes
  • Vous compilez ces lignes de code avec C-c C-c
  • Vous allez tester si tout fonctionne bien dans le REPL, C-c C-z pour afficher le REPL, ou bien même C-c C-y déjà générer la base de l'appel de fonction.
  • On retourne dans le buffer où se situe notre code (pas de raccourci SLIME à ma connaissance pour cela, hélas)
  • Si vous faites plusieurs modifications dans un fichier et que vous ne savez plus trop ce que vous avez modifié, un petit C-c C-l permettra de recharger le fichier dans le REPL (faire un =(load "file.lisp"=))
  • Lorsqu'un nom de fonction vous échappe, l'autocomplétion vous aidera sûrement (M-TAB)
  • Si vous recherchez la définition d'une fonction, dont le nom est situé sous le point, M-. est là pour ça (ceci fonctionne même avec les fonctions définies par l'implémentation, si vous avez les sources d'installées).

5 Paredit

Paredit est un mode mineur (qu'on peut donc charger en même temps que d'autres modes) permettant de manipuler plus facilement les expressions parenthésées. Cela est donc très pratique pour ce qui est de l'édition de code Lisp. Néanmoins son utilisation peut être assez rebutante au début, donc n'hésitez pas à prendre SLIME et Emacs bien en main avant d'utiliser Paredit.

5.1 Installation & Configuration

Pour installer paredit, il suffit de le récupérer depuis l'Emacs Wiki et de le require dans le .emacs:

(require 'paredit)

On ajoute aussi un hook pour automatiquement le charger quand on édite du Common Lisp, ainsi que pour utiliser la fonction paredit-newline lorsqu'on appuye sur la touche Entrée:

(add-hook 'slime-mode-hook (lambda () (paredit-mode t)))
(add-hook 'slime-mode-hook
          (lambda () (local-set-key (kbd "RET") 'paredit-newline)))

5.2 Utilisation

Pour la liste des raccourcis de Paredit, référez-vous à la Reference Table ansi qu'à la Cheatsheet. Tout ce qui sera dit ici concerne aussi bien les parenthèses que les crochets et les guillemets. Il y a différents types de commandes disponibles dans paredit.

5.2.1 Les commandes d'insertion

Elles sont bindées directement à la touche correspondante (donc quand vous tapez une parenthèse ouvrante, la fonction paredit-open-round est appelée et se charge de créer la parenthèse fermante correspondante et de placer le curseur entre les deux parenthèses). C'est le cas des touches (, ), [, ], =". =\

5.2.2 Commandes de suppression

Vous remarquerez vite que vous ne pouvez pas supprimer une parenthèse comme vous le pouviez avant d'utiliser paredit. Effectivement, paredit garde les parenthèses équilibrées en permanence. Pour supprimer une parenthèse, il faut alors supprimer la parenthèse équivalente, ou simplement ne pas la supprimer parce que ce n'est pas ce que vous souhaitez (vous souhaitez par exemple la déplacer).

Vous remarquerez aussi que le comportement de C-k a changé. Il supprime désormais tout jusqu'à la fin de la s-exp ou jusqu'à la fin de la ligne (en fonction de ce qui se termine d'abord). M-d permet maintenant de supprimer des mots sans supprimer les parenthèses entre le point et le mot à supprimer.

5.2.3 Commandes de changement de profondeur

J'utilise principalement M-<up> et M-<down>, pour détruire la s-exp au niveau du point, en gardant ce qui se trouve entre la parenthèse ouvrante et le point (ou entre le point et la parenthèse fermante)

5.2.4 Barfage et Slurpage

Sous ces termes étranges se cache une des features les plus utiles de Paredit. Le slurpage correspond au fait d'étendre la s-exp en slurpant le mot suivant/précédant la s-exp (en le faisant entrer dans la s-exp). Ceci ce fait avec C-( et C-). Le barfage correspond quand à lui à l'inverse du slurpage, il permet donc de faire sortir un mot de la s-exp courante. Le slurpage ce fait avec C-{ et C-}

6 La documentation

6.1 Sites internet

Il y a principalement deux ressources indispensables pour la programmation en Common Lisp:

  • Le livre de référence Common Lisp The Language, dans sa seconde édition (abrégé CLTL2). Ce livre est disponible en ligne ici.
  • Le Common Lisp Hyperspec, qui est en fait une version en ligne du standard ANSI Common Lisp.

Ces deux ressources devraient vous suffire la plupart du temps. Vous pouvez aussi avoir recourt à la documentation de votre implémentation Common Lisp (1, que je recommande plutôt qu'une autre implémentation).

Pour une recherche plus facile, il existe le site l1sp.org qui recherche à la fois dans l'Hyperspec, le CLTL2 ainsi que diverses autres documentations.

6.2 Fonctions standards

Plutôt que d'aller regarder dans la documentation online, quand vous avez oubliez quelque chose ou ne savez pas ce que fait une fonction ou ce que contient une variable, le standard défini quelques fonction assez pratiques.

6.2.1 describe

describe est une fonction qui prend un paramètre: l'objet à propos duquel vous souhaitez obtenir plus d'informations. describe affichera alors:

  • La valeur de cet objet
  • Le type de l'objet
  • Dans le cas où l'objet est un symbole, toutes les valeurs auxquelles l'objet est lié (un symbole peut représenter plusieurs choses en Common Lisp, par exemple une variable et une fonction en même temps)
  • Dans le cas où l'objet est une fonction, diverses informations pratiques telles que sa lambda-list, sa documentation, le nom du fichier dans lequel la fonction est définie, …

Par exemple:

CL-USER> (describe 'describe)
COMMON-LISP:DESCRIBE
  [symbol]

DESCRIBE names a compiled function:
  Lambda-list: (OBJECT &OPTIONAL (STREAM-DESIGNATOR *STANDARD-OUTPUT*))
  Declared type: (FUNCTION (T &OPTIONAL (OR STREAM (MEMBER T NIL)))
                  (VALUES &OPTIONAL))
  Documentation:
    Print a description of OBJECT to STREAM-DESIGNATOR.
  Source file: SYS:SRC;CODE;DESCRIBE.LISP.NEWEST

6.2.2 documentation

La fonction documentation prend en argument un objet et un symbole indiquant le type de documentation que l'on souhaite, et retourne la chaîne de caractère correspondant à documentation de l'objet. Le symbole peut par exemple être function pour obtenir la documentation de la fonction associée à l'objet, ou variable, ou type, structure, etc. Pour plus d'info, faites un (describe 'documentation).

Par exemple:

CL-USER> (documentation 'describe 'function)
"Print a description of OBJECT to STREAM-DESIGNATOR."

6.2.3 apropos

Quand vous vous souvenez vaguement d'un nom de fonction mais que vous ne parvenez pas à retrouver le nom complet, vous pouvez utiliser apropos pour vous aider. apropos prend en argument un symbole ou une chaîne de caractère et retourne tous les symboles existants (qui ont une valeurs) dont le nom correspond au paramètre. Par exemple:

CL-USER> (apropos "apropos")
APROPOS (fbound)
APROPOS-LIST (fbound)
QL-DIST:SYSTEM-APROPOS (fbound)
SWANK:APROPOS-LIST-FOR-EMACS (fbound)
SWANK::APROPOS-SYMBOLS (fbound)
SWANK::MAKE-APROPOS-MATCHER (fbound)

6.2.4 inspect

inspect est une fonction assez pratique qui permet de regarder en détail de quoi est composé un objet. Cette fonction affichera le type ainsi que certaines caractéristiques de l'objet, et permettra de inspect-er certaines propriété de cet objet. Un exemple sera plus parlant:

CL-USER> (inspect 'describe)

The object is a SYMBOL.
0. Name: "DESCRIBE"
1. Package: #<PACKAGE "COMMON-LISP">
2. Value: "unbound"
3. Function: #<FUNCTION DESCRIBE>
4. Plist: NIL
> 3

The object is a FUNCTION named DESCRIBE.
0. Lambda-list: (SB-IMPL::OBJECT &OPTIONAL
                 (SB-KERNEL:STREAM-DESIGNATOR *STANDARD-OUTPUT*))
1. Ftype: (FUNCTION (T &OPTIONAL T) (VALUES &OPTIONAL))
> 0

The object is a proper list of length 3.
0. 0: SB-IMPL::OBJECT
1. 1: &OPTIONAL
2. 2: (SB-KERNEL:STREAM-DESIGNATOR *STANDARD-OUTPUT*)
> q

6.3 SLIME

SLIME n'apporte en soi rien de nouveau par rapport à ce qui a été décrit jusqu'ici dans cette section. Néanmoins, le résultat produit par SLIME est souvent plus agréable et plus facilement manipulable. De plus, comme toujours, SLIME a des raccourcis pour tout ceci.

Le préfixe des raccourcis liés à la documentation est C-c C-d. Les raccourcis utiles sont:

  • C-c C-d d décrit (avec describe) le symbole au niveau du point
  • C-c C-d f décrit la fonction au niveau du point
  • C-c C-d a recherche un terme avec apropos (le terme est demandé dans le minibuffer)
  • C-c C-d h recherche un symbole dans l'Hyperspec et ouvre la page correspondante avec votre navigateur

6.3.1 SLIME inspector

Plutôt que d'utiliser inspect, il existe le SLIME inspector qui est un peu plus convivial. Pour le lancer, il faut faire C-c I et entrer ce que l'on souhaite inspecter. Il suffit ensuite de se déplacer sur l'objet que l'on souhaite détailler, et d'appuyer sur RET. Pour revenir en arrière, il suffit d'appuyer sur l. La liste des raccourcis disponibles se trouve dans la documentation de SLIME.

7 Les outils de débuggage

Si votre code ne fonctionne pas, ANSI Common Lisp nous propose quelques mécanismes afin de faciliter le débuggage.

7.1 trace

La première chose qui vient à l'esprit pour débugger du code est de voir comment il fonctionne. Pour cela, on a naturellement tendance à balancer des format un peu partout pour afficher les résultats. La fonction trace nous permet d'éviter de faire cela dans la plupart des cas. Elle permet de tracer les appels aux fonctions voulues, et d'afficher leurs arguments ainsi que les valeurs de retours. Par exemple:

CL-USER> (defun fac (n) 
           (if (= n 0) 
               1
               (* n (fac (1- n)))))
FAC
CL-USER> (trace fac)
(FAC)
CL-USER> (fac 5)
  0: (FAC 5)
    1: (FAC 4)
      2: (FAC 3)
        3: (FAC 2)
          4: (FAC 1)
            5: (FAC 0)
            5: FAC returned 1
          4: FAC returned 1
        3: FAC returned 2
      2: FAC returned 6
    1: FAC returned 24
  0: FAC returned 120
120

Pour arrêter de tracer une fonction, il faut utiliser untrace (avec comme argument la fonction à arrêter de tracer, ou rien pour ne plus tracer aucune fonction).

7.2 Invoquer le debugger

Lorsqu'une erreur se produit, Common Lisp nous lache dans le debugger, afin de choisir que faire. On a alors la possibilitée de choisir un restart, en entrant le numéro correspondant.

La façon la plus simple d'invoquer le debugger est d'utiliser error, qui attends en paramètre une format string (comme format). Par exemple:

(defun fac (n)
  (cond ((< n 0) (error "Can't calculate the factorial of a negative number: ~a" n))
        ((= n 0) 1)
        ((> n 0) (* n (fac (1- n))))))

7.3 Single stepping

Si vous êtes familiers avec gcc, vous serez plus habitués au single stepping. Bien que cette technique n'est pas tellement utilisée en Lisp, elle peut s'avérer pratique dans certains cas.

Pour utiliser cela, il suffit d'appeler step avec comme argument le code à évaluer. Ensuite, vous êtes lachés dans le stepper, qui attends des commandes similaires à gcc (next, step, …). L'utilisation du stepper dépend de l'implémentation que vous utilisez, la plupart du temps, taper un simple help ou ? vous permettra de connaître la liste des commandes supportées.

7.4 Les conditions et les restarts

Common Lisp implémente un mécanisme assez puissant, similaire aux exceptions d'autres langages, ce sont les conditions. Common Lisp implémente aussi les /restarts/, chose assez unique, qui permet de laisser le choix à l'utilisateur (ou au programme) de quelle solution choisir dans le cas d'erreurs etc. Ces mécanismes sont trop complexes pour être définis en quelques paragraphes et ne seront donc pas détaillés ici. Pour un exemple d'utilisation assez bien foutu, lisez ceci.

7.5 SLIME

SLIME rend l'utilisation du debugger ou du stepper plus agréable. Je n'ai pas décrit l'utilisation du debugger en détail jusqu'ici, car elle dépend souvent de l'implémentation choisie. SLIME unifie donc la chose, et la rend plus user-friendly (plus d'informations affichés, des simples raccourcis à taper, etc.).

Pour invoquer un restart, il suffit d'entrer le numéro correspondant, où d'utiliser c ou a, respectivement pour les restarts continue et abort.

Dans le cas du single stepping, les différentes options vous sont présentées sous forme identique à celle des restarts. De plus, SLIME montre où vous vous situez dans le code, grâce au buffer *slime-source*.

Le SLIME debugger nous donne aussi la possibilitée d'agir sur les frames assez facilement. Une frame correspond à l'état de l'image lors d'un appel de fonction. Les frames sont listées dans la section commençant par Backtrace: dans le SLIME debugger. Pour agir sur une frame, déplacez-vous d'abord dessus (avec les flèches, la souris ou n ou p).

Ensuite, vous pouvez:

  • afficher plus de détail sur la frame (RET ou t)
  • évaluer du code dans cette frame (e ou i pour inspecter le résultat après l'évaluation)
  • voir où est définie la fonction (v)
  • recompiler la fonction correspondante (C-c C-c)

Pour plus d'informations sur le SLIME debugger, voir la partie correspondante dans le manuel de SLIME.

8 Bibliothèques

Common Lisp possède de nombreuses bibliothèques, couvrant de nombreux domaines d'applications. Le meilleur endroit pour trouver une bibliothèque correspondant à ce que vous cherchez est sur cliki, et spécialement sur la page reprenant les bibliothèques recommandées actuellement.

8.1 Installation de bibliothèques

8.1.1 Quicklisp

La méthode conventionnelle pour installer des bibliothèques en Common Lisp est de passer par asdf-install, néanmoins Quicklisp a vu le jour récemment et est mieux fait que asdf-install, plus simple d'utilisation, et est en voie de remplacer asdf-install.

Pour installer Quicklisp, suivez les instructions données sur le site de Quicklisp. Ensuite, pour charger une bibliothèque (et l'installer si elle ne l'est pas déjà), il suffit d'appeler ql:quickload avec le nom de la bibliothèque.

Par exemple:

CL-USER> (ql:quickload "hunchentoot")
To load "hunchentoot":
  Load 1 ASDF system:
    hunchentoot
; Loading "hunchentoot"
..................................................
[package bordeaux-threads]........................
[package split-sequence]..........................
[package usocket].................................
[package rfc2388].................................
[package md5].....................................
[package trivial-gray-streams]....................
[package flexi-streams]...........................
..................................................
..................................................
[package alexandria.0.dev]........................
[package babel-encodings].........................
[package babel]...................................
[package cffi-sys]................................
[package cffi]....................................
[package cffi-features]...........................
[package cl+ssl]..................................
[package cl-fad]..................................
[package cl-fad-test].............................
[package cl-base64]...............................
[package chunga]..................................
[package url-rewrite].............................
[package hunchentoot].....................
("hunchentoot")

8.1.2 Autres bibliothèques

Certaines bibliothèques ne sont pas inclues dans Quicklisp (car elles ne sont pas assez connues ou juste trop récentes). Il faudra alors les installer à la main. Pour ce faire, récupérez les sources (une archive ou bien un clone du dépôt git, hg, …), placez les par exemple dans le dossier ~/.sbcl/site/, et liez les fichiers .asd dans ~/.sbcl/systems/.

Par exemple, pour installer hunchentoot sans passer par Quicklisp (ceci n'est qu'à titre d'exemple, hunchentoot étant disponible dans Quicklisp):

cd ~/.sbcl/site/
wget http://weitz.de/files/hunchentoot.tar.gz
tar xvf hunchentoot.tar.gz
rm hunchentoot.tar.gz
ln -s ~/.sbcl/site/hunchentoot/*.asd ~/.sbcl/systems/

Ensuite, la bibliothèque pourra être chargée par Quicklisp ou ASDF.

9 Les livres & articles

Utiliser les bons outils ne suffit pas, il faut aussi savoir coder en Lisp. Pour cela, les livres peuvent d'être d'une grande aide. Cette section reprends donc les bons livres traitant ou utilisant le Common Lisp.

9.1 Apprentissage du Common Lisp

Les deux bons livres pour débuter l'apprentissage du Common Lisp sont:

  1. Le Land of Lisp, qui présente le Lisp à travers de nombreux exemples assez amusants, dans un style particulier. Le livre est assez récent et n'est pas trop cher.
  2. Le Practical Common Lisp, un peu plus ancien mais toujours d'actualité, qui présente aussi le Lisp au travers de nombreux exemples pratiques. Celui-ci est lisible gratuitement en ligne.

Deux autres livres souvent conseillés sont Common Lisp: A Gentle Introduction to Symbolic Computation et Successful Lisp, tous deux disponibles gratuitement en version électronique. Le chapitre 2 de Successful Lisp donne une liste de chapitres à lire suivant vos connaissances actuelles.

9.2 Livres plus avancés

Une fois que vous aurez bien appréhendé les principes du Lisp et que vous jonglerez avec les parenthèses, vous pouvez approfondir certains principes:

  • Les macros, et ainsi explorer au plus profond la méta-programmation, tout d'abord avec On Lisp (disponible gratuitement), ensuite avec Let Over Lambda (plus de la moitié du livre est disponible gratuitement).
  • Le CLOS, avec le livre Object-Oriented Programming in Common Lisp: A Programmer's Guide to CLOS de Sonya E. Keene, qui présente de façon assez détaillée le CLOS, et mets les principes en pratique au travers de quelques exemples.

9.3 Autres

Toujours dans les livres:

Il y a aussi de nombreux articles traitant des principes fondamentaux du Lisp, par exemple:

D'autres livres sont listés sur la page associée du cliki.

10 Conclusion

Nous avons donc vu ici un bon nombre de techniques et d'outils adaptés au développement de programmes en Common Lisp. Bien évidemment, tout ce qui a été vu ici ne doit pas spécialement être utilisé, bien que cela puisse rendre parfois la vie bien plus facile. Cet article n'est en rien une référence pour aucun des sujets présentés ici, pour cela, référez-vous à la documentation correspondante (dont les liens sont fournis dans cet article).

Footnotes:

1 La documentation de SBCL est aussi consultable avec info(1) (ou dans GNU Emacs avec M-x info), et est peut-être installée en html ou en pdf avec votre installation de SBCL.

Author: Quentin Stievenart <>

Date: 2011-01-26 22:53:49 CET

HTML generated by org-mode 6.33x in emacs 23

Apprendre la programmation fonctionnelle avec Scheme

UP | HOME

Apprendre la programmation fonctionnelle avec Scheme

Table of Contents

  • 1 Introduction
  • 2 Les outils
    • 2.1 Le système d'exploitation
    • 2.2 L'éditeur
      • 2.2.1 Vim
        • 2.2.1.1 Installation
        • 2.2.1.2 Utilisation
        • 2.2.1.3 Liens
      • 2.2.2 GNU Emacs
        • 2.2.2.1 Installation
        • 2.2.2.2 Utilisation
        • 2.2.2.3 Intégration de Scheme avec quack
        • 2.2.2.4 Le mode paredit
        • 2.2.2.5 Liens
    • 2.3 L'implémentation
      • 2.3.1 Racket
      • 2.3.2 Guile
      • 2.3.3 Scsh
      • 2.3.4 Bigloo, Gambit et Chicken
  • 3 Les ressources pour apprendre
    • 3.1 Les livres
    • 3.2 Les spécifications
    • 3.3 La documentation
    • 3.4 La communauté
  • 4 À propos de ce document
    • 4.1 Modifications

1 Introduction

Ce document regroupe un ensemble de conseils et de liens vers des ressources externes afin de faciliter l'apprentissage de la programmation fonctionnelle avec Scheme.

Ce document s'adresse à toute personne intéressée par la programmation fonctionnelle (ou par la programmation en général) et ne requiert pas spécialement de connaissances en programmation.

Il faudra néanmoins un niveau correct d'anglais (savoir comprendre un texte avec des termes spécifiques à l'informatique) afin de pouvoir exploiter les ressources données ici. Bien qu'il existe de nombreux documents en français, la plupart sont en anglais et la connaissance de l'anglais est plus que nécessaire de nos jours.

2 Les outils

2.1 Le système d'exploitation

Préférez un système d'exploitation de type Unix et libre. En effet, ces systèmes, encore peu adapté au grand public il y a peu, sont créés « par des développeurs, pour des développeurs » et représentent donc un environment plus adapté à la programmation.

2.2 L'éditeur

Là où vous passerez le plus de temps lorsque vous programmerez en Scheme, c'est dans votre éditeur, alors, si ce n'est déjà fait, apprenez à vous servir d'un « vrai » éditeur de texte, à savoir GNU Emacs ou Vim (bien qu'il existe de nombreux autres éditeurs de texte corrects, ces deux là sont sûrement les plus adaptés à la programmation). L'utilisation de Vim risque d'être un poil difficile pour les néophytes du fait que ce soit un éditeur modal, contrairement à GNU Emacs. De plus, GNU Emacs possède des modes adaptés à la programmation dans divers langages dérivés du Lisp et bien qu'il soit possible d'avoir un résultat similaire avec des scripts pour Vim, les modes d'Emacs sont plus souples et plus puissants. Nous vous conseillons donc GNU Emacs, quitte à ne l'utiliser que pour le développement en Scheme ou autre langage de type Lisp.

Si vous utilisez Racket, vous aurez sûrement installé drracket, un IDE 1.

Les deux prochaines sections présentent Vim et GNU Emacs, si vous ne souhaitez pas utiliser ces éditeurs, libre à vous de passer ces sections, mais sachez que ces éditeurs permettent un développement plus aisé, voire souvent plus rapide.

2.2.1 Vim

2.2.1.1 Installation

La plupart des distributions GNU/Linux incluent Vim dans leur dépôts logiciels, vous pouvez donc l'installer simplement comme vous installeriez n'importe quel logiciel. Pour les systèmes de type BSD contenant un arbre des ports, le port contenant Vim est la plupart du temps editors/vim. Il faut noter que la plupart de ces systèmes possèdent déjà vi d'installé, mais utilisez plutôt Vim.

Pour Windows ainsi que Mac OS, installez Vim depuis le site officiel.

2.2.1.2 Utilisation

Une fois Vim installé, vous pouvez de suite apprendre à vous en servir en lançant vimtutor (soit via divers menu (pour Windows notamment), soit directement dans un shell). Notez que vimtutor est aussi disponible en français, en lançant simplement `vimtutor fr` pour les Unix.

Gvim est aussi disponible et permet d'utiliser plus facilement Vim dans un premier temps, grâce aux menus, n'hésitez donc pas à l'utiliser au début mais n'oubliez pas que pour profiter au mieux de la puissance de Vim, il est mieux d'apprendre les commandes plutôt que de passer par la souris.

2.2.1.3 Liens

2.2.2 GNU Emacs

2.2.2.1 Installation

Tout comme Vim, GNU Emacs se trouve inclut dans la plupart des distribution GNU/Linux dans le paquet emacs, ainsi que dans l'arbre des ports des BSD, dans le port editors/emacs. Vous pouvez aussi récupérer GNU Emacs sur le site de GNU.

2.2.2.2 Utilisation

Au premier lancement d'Emacs, celui ci devrait vous afficher différent liens, dont un vers le tutoriel. Si ce n'est pas le cas, vous pouvez lancer ce tutoriel par le raccourci C-h t (control-h suivit de t).

Nous ne parlerons pas ici de la customization d'Emacs, pour plus d'informations à ce propos, référez vous à la documentation de GNU Emacs.

2.2.2.3 Intégration de Scheme avec quack

Il existe un mode pour GNU Emacs qui permet de se simplifier la vie lorsqu'on développe en Scheme: quack.

Pour l'installer, placez le fichier téléchargé dans un dossier (~/elisp/ par exemple) et ajoutez ceci à votre ~/.emacs:

(add-to-list 'load-path "~/elisp/")
(require 'quack)

Avec quack chargé, vous aurez de nouveaux raccourcis claviers, spécifiques à quack, plus ou moins pratiques selon les cas:

  • C-c C-q m -- Voir un « manuel » dans votre navigateur (ceci comprends la documentation de plusieurs interpréteurs ainsi que certains livres)
  • C-c C-q k -- Voir la documentation d'un mot-clé (uniquement pour Racket)
  • C-c C-q s -- Voir une SRFI 2
  • C-c C-q r -- Lance un interpréteur Scheme, afin de pouvoir interagir avec
  • C-c C-q f -- Visite un fichier (comme C-x C-f) en utilisant le nom du fichier situé sous le point si cela est possible
  • C-c C-q l -- Transforme une définition de fonction de la forme (define (foo bar) baz) vers la forme (define foo (lambda (bar) baz))
  • C-c C-q t -- Formate correctement tout le buffer (indentation etc.).

Vous aurez aussi accès à de nombreux raccourcis récurrents dans les autres modes (notamment avec SLIME ou dans l'emacs-lisp-mode) permettant de simplifier l'évaluation des s-expressions:

  • C-c C-z – Se déplace dans le buffer *scheme*
  • C-c C-e -- Évalue la s-expression se terminant au point 3
  • C-c C-r -- Évalue la région
  • C-c M-r -- Évalue la région et se déplace dans le buffer *scheme* (équivalent à C-c C-r suivi de C-c C-z
  • C-c C-e – Évalue la dernière définition
  • C-c M-e -- Évalue la dernière définition et se déplace dans le buffer *scheme*
  • C-c M-c -- Compile la définition située au niveau du point 4
  • C-c C-c -- Compile la définition située au niveau du point et se déplace dans le buffer *scheme*
  • C-c C-l -- Charge un fichier Scheme via la fonction load
  • C-c C-k -- Compile et charge un fichier Scheme via la fonction compile-file

Grâce à ces raccourcis, vous pouvez donc tester votre code tout au long du développement, ré-évaluer les bouts de codes changés et retester le tout, ce qui est bien plus rapide que de compiler ou évaluer l'entièreté du logiciel à chaque petit changement.

2.2.2.4 Le mode paredit

Ce mode facilite grandement l'utilisation de parenthèses et est donc très pratique pour éditer du Scheme. Néanmoins, il peut être fortement dérangeant et difficile à prendre en main, n'hésitez donc pas à le désactiver s'il ne vous plait pas.

Pour l'installation, téléchargez paredit.el ici, placez le dans le même dossier que quack.el, et ajoutez ceci à votre .emacs:

(require 'paredit)
(add-hook 'scheme-mode-hook 'paredit-mode)

Pour la liste des raccourcis et leurs explications, référez vous à la cheatsheet de paredit.

2.2.2.5 Liens

Quelques liens supplémentaires:

2.3 L'implémentation

Il existe plusieurs implémentations de Scheme, la plupart respectant le standard R5RS ou R6RS, incluant de nombreuses SRFI et ayant chacune des spécificités. Il est impossible de lister ici toutes les implémentations de Scheme5 mais voici les implémentations principales:

2.3.1 Racket

Racket6 est sûrement l'implémentation de Scheme avec le plus d'outils et de bibliothèques disponibles, citons par exemple un serveur Web, une GUI, des bindings OpenGL, une bibliothèque pour le rendu HTML, …

Vous pourrez donc développer toutes sortes de logiciels en Scheme avec Racket. Cela est encore plus facilité grâce à PLaneT qui regroupe beaucoup de paquets créés par les utilisateurs de Racket. Notez que la documentation est extrêmement complète, tant au niveau du standard Scheme qu'aux spécificités de Racket.

2.3.2 Guile

Guile, en plus de fournir un interpréteur avec un grand nombre de SRFI, permet d'embarquer Scheme dans des applications C comme langage de script. Il est notamment utilisé dans LilyPond, GNU TeXmacs, GnuCash

2.3.3 Scsh

Scsh permet l'utilisation de Scheme comme langage de script au même titre que bash, zsh ou tout autre shell.

2.3.4 Bigloo, Gambit et Chicken

Ces trois implémentations sont des compilateurs Scheme vers C et permettent donc d'obtenir des programmes binaires à partir du code source Scheme. Ils permettent de communiquer entre Scheme et C.

3 Les ressources pour apprendre

Avant tout, sachez que la meilleure méthode pour apprendre un langage n'est pas de lire des livres, mais de pratiquer. Certes la lecture de livres vous aiguillera sur le bon chemin, mais rien ne pourra remplacer la pratique qui vous permettra d'assimiler le langage et d'apprendre de vos erreurs.

3.1 Les livres

Nous allons présenter ici uniquement les livres libres à la consultation, bien qu'il en existe d'autres très bons. Notez que la plupart des livres ici peuvent aussi être achetés en version imprimée.

Il existe deux sortes de livres traitant du Scheme:

  1. ceux qui utilisent Scheme comme langage de programmation au cours du livre, mais dont le sujet principal n'est pas le Scheme
  2. ceux dont le sujet principal est le Scheme.

Intéressons nous d'abord à la première catégorie, plus souvent orientée vers les débutant mais qui présente des concepts fondamentaux souvent non maîtrisés par beaucoup de développeurs. Les livres suivants en font partie:

Si vous avez apprécié le chapitre 4 de SICP 7 ou le chapitre 10 de Concrete Abstraction, vous aimerez aussi Programming Languages: Application and Interpretation: tout au long du livre vous allez mettre en place un interpréteur en Scheme pour un langage assez proche du Scheme. L'approche est légèrement différente entre les trois livres, mais de nombreux points se recoupent.

Passons maintenant à la deuxième catégorie, c'est-à-dire les livres traitant du langage Scheme. Préférez ces livres là si vous avez déjà des connaissances en programmation fonctionnelle:

Sachez enfin que le Scheme Cookbook regroupe de nombreuses recettes (simples programmes destinés à effectuer des tâches précises) très pratiques.

3.2 Les spécifications

Les spécifications du langage Scheme sont très précises et compréhensible, donc n'hésitez pas à les parcourir.

La dernière spécification en date et le R⁶RS, sortie en 2007. La plupart des implémentations utilisent encore R⁵RS et certaines le R⁴RS.

3.3 La documentation

Vous pouvez aussi trouver énormément d'informations sur la documentation de votre interpréteur, n'hésitez donc pas à la parcourir en cas de problème (dans de nombreux cas vous pouvez même vous référez à la documentation d'autres interpréteurs).

3.4 La communauté

Dernièrement, sachez qu'il existe une assez grande communauté de Schemers que vous pouvez trouver à divers endroits:

  • sur IRC: le channel #scheme sur irc.freenode.org
  • sur le newsgroup comp.lang.scheme
  • sur la/les mailing-list(s) de votre interpréteur
  • sur de nombreux sites webs:
    • schemers.org regroupe un ensemble de ressources en rapport avec le Scheme
    • Le planet Scheme regroupe divers blogs à propos de Scheme
    • Community Scheme Wiki est un wiki regroupant de nombreuses ressources sur le Scheme
    • L'exceptionnel Lambda the Ultimate regroupe une communauté de passionné de programmation et bien qu'il ne soit pas seulement orienté vers le Scheme, la plupart des ressources disponibles sur les articles du blog ou sur le forum sont d'une très grande qualité (et parfois d'un niveau un peu élevé)
    • … et sur la plupart des liens donnés sur cette page.

4 À propos de ce document

Ce document a été formaté grâce à org-mode.

Vous êtes autorisés à utiliser, modifier, distribuer ce document sous les termes le la licence Creative Commons Attribution 2.0 (CC-BY).

Tout commentaire est le bienvenu, par mail à acieroid -at- awesom -dot- eu.

4.1 Modifications

  • 31 août 2009: mise en ligne du document
  • 14 mars 2010: passage du document sous pandoc, corrections de plusieurs liens.
  • 20 août 2010: conversion du document en .org, diverses corrections, mise à jour de tout ce qui est relatif à Racket (anciennement PLT-Scheme)

Footnotes:

1 Integrated Development Environment, Environnement de Développement Intégré en français, c'est-à-dire un programme contenant principalement un éditeur ainsi que divers outils afin de faciliter le développement d'applications.

2 : Les SRFI, pour Scheme Requests for Implementation, sont des bibliothèques qui sont en quelques sortes standardisées qui ajoutent de nombreuses fonctionnalités au standard R6RS. Ces bibliothèques se retrouvent dans de nombreuses implémentations de Scheme et permettent donc d'avoir un ensemble de bibliothèques communes à chaque implémentation plutôt que d'avoir des bibliothèques spécifiques à chaque implémentation. Il en existe à ce jour 72 en version finale.

3 : le point dans Emacs signifie la position actuelle du curseur, voir le manuel d'Emacs

4 : La compilation d'une s-expression se fait via la fonction compile, qui n'est pas présente dans toutes les implémentations de Scheme. Si votre implémentation de Scheme n'en possède pas, vous pouvez la définir comme suit, afin de simplement évaluer l'expression (ce raccourci devient alors l'équivalent de C-c C-e mais C-c C-c est alors plus pratique que C-c M-e, car plus facile à taper: (define compile (lambda (sexp) (eval sexp)))

5 : vous pouvez trouver une liste d'implémentations ici

6 : Racket n'est plus vraiment un Scheme à proprement parler, depuis le changement de nom (ils s'appelaient avant PLT-Scheme) qui a accompagné la version 5, certains changements l'ont rendu incompatible avec Scheme, mais la majeure partie du langage reste du Scheme, c'est pourquoi dans la suite du document nous le considérerons comme un Scheme.

7 : Structure and Interpretation of Computer Programs

Author: Quentin Stievenart <>

Date: 2010-09-17 23:23:05 CEST

HTML generated by org-mode 6.33x in emacs 23

Robotime

UP | HOME

Robotime

Robotime is a robots-like game made with Common Lisp. It is made for the 2010 International Lisp Games Expo and for the Novendiales(fr) (the theme was "time control"). A screenshot is available here.

Table of Contents

  • 1 Install and play
    • 1.1 Dependencies
      • 1.1.1 Alexandria
      • 1.1.2 Until It Dies
    • 1.2 Robotime
      • 1.2.1 Keybindings
  • 2 Gameplay
    • 2.1 Time control
    • 2.2 Bonus
    • 2.3 The grid
  • 3 Conclusion
    • 3.1 Lisp
    • 3.2 Libraries
    • 3.3 Graphic Stuff
  • 4 Contact

1 Install and play

1.1 Dependencies

You need a correct Common Lisp implementation (look at SBCL for Unix-based systems and Clozure CL for Windows or OS X). Also, be sure to have ASDF installed (provided with SBCL, don't know for Clozure CL). There are only two libraries needed for robotime: Alexandria and Until It Dies.

1.1.1 Alexandria

You can easily install Alexandria with ASDF-Install:

(require :asdf)
(require :asdf-install)
(asdf-install:install 'alexandria)

1.1.2 Until It Dies

Since Until It Dies is a fairly new library, it's only available through the git repo at github, and robotime needs its devel branch. Basically, here are the steps to install it (on an Unix-based system):

$ cd ~/.sbcl/site/
$ git clone -b devel http://github.com/sykopomp/until-it-dies.git
$ ln -s `pwd`/until-it-dies/*.asd ~/.sbcl/systems/

You'll also need to install UID's dependencies:

Of course you'll need to install devil, openal, the opengl libs, and ftgl.

If you encounter any problem with UID, you might try to use the commit 18c4895bc2bc0512a4425ab2ef23550b186ac175 (with git checkout).

1.2 Robotime

You can now install robotime:

$ hg clone http://hg.awesom.eu/robotime/
$ ln -s `pwd`/robotime/robotime.asd ~/.sbcl/systems/

And to launch it:

$ cd robotime
$ ./robotime.sh

If you get a CL-DEVIL:COULD-NOT-OPEN-FILE condition, you can adapt the variable *resources-dir* in graphic.lisp, eg. :

(defparameter *resources-dir* #p"/home/user/robotime/resources/")

1.2.1 Keybindings

ActionKey
Move northr
Move southv
Move westd
Move eastg
Move north-weste
Move north-eastt
Move south-westc
Move south-eastb
Forward the timef
Backward the timey
Use a blastspace
Quitescape

2 Gameplay

Robotime is a robots-like game. Robots is an old turn-based game (you can test the original one by installing bsdgames if you're under Linux) with a very simple principle:

Robots is played on a two-dimensional rectangular grid. The objective of the game is to escape from a number of robots, which have been programmed with only a single objective: to kill the player. – Wikipedia

Robotime has some differences with the original robots games:

2.1 Time control

You can control the time in both directions (forward and backward). If you go back in the time, robots will backtrack and you'll lose some power. If you advance the time, robots will move in your direction and you'll gain power. You can also gain power with some bonus. When you control the time you can't move, so be carefull not to be caught by robots. If you die, you'll simply be teleported somewhere else on the grid.

2.2 Bonus

There are two kinds of bonus in the game, which appear and disappear randomly if the player does not take them:

  • A power bonus which increases your power
  • A blast bonus which gives you one more blast. You can use a blast by pressing space, it'll blow some robots that are near you.

When you go back in the time, bonus may reappear if you didn't take them.

2.3 The grid

The grid is a 20x40 isometric grid.

3 Conclusion

3.1 Lisp

Lisp is very useful for rapid game prototyping. The game had to be done in nine days, but after 2 days it was already playable (with awful graphics). CLOS is very nice to use and provides useful things (around methods etc.). Also, macros allows you to save a lot of lines of code. For example, here's how the power bonus is defined:

(new-bonus power "bonus.png"
  (add-power player 10))

And here's how it would be defined without the new-bonus macro:

(defvar *power-bonus-tile* (load-image "bonus.png"))
(defclass power-bonus (bonus) 
  ())
(defmethod draw ((bonus power-bonus))
  (when (alivep bonus)
    (draw-at (x bonus) (y bonus) *power-bonus-tile*)))
(defmethod collision ((player player) (bonus power-bonus))
  (when (alivep bonus)
    (add-power player 10)))
(push 'bonus *bonus*)
(incf *n-bonus*)

It might be ok for one or two bonus only, but with the new-bonus macro we can define easily a lot of new bonuses, without repeating everytime the same code.

3.2 Libraries

Common Lisp has a lot of libraries, but when it comes to games related libraries, there are only two or three libraries. Or rather, there were two or three libraries, because those dto's ILGE has motived some people to work on games and games related libraries, and now there are much more good games libs and bindings (see here). Even if most of them are not officially released and still in developpement, they're quite usable, and if you encounter some bugs you can still contact the authors on #lisp or #lispgames.

3.3 Graphic Stuff

3b's entry made me discover OpenGameArt.org. Those kinds of site are usefull, but really lack of content. I know a bit of gimp, inkscape and blender, but I don't have the knowledge to create my own graphic stuff. I think graphics are what free games miss the most, and sites like OpenGameArt might help with that, but apparently they're not enough known from graphists.

The graphics I used are:

4 Contact

Any feedback, suggestions, bug reports, etc. is welcome at #lispgames (irc.freenode.org) or in french at #GCN (irc.langochat.org).

Author: Quentin Stievenart <>

Date: 2010-09-17 11:54:53 CEST

HTML generated by org-mode 6.33x in emacs 23

Les macros en Common Lisp: fonctions anonymes raccourcies

Les macros en Common Lisp: fonctions anonymes raccourcies

Cet article a pour but de présenter les macros de Common Lisp au travers d'un exemple concret, qui permettra peut-être à certaines personnes de mieux comprendre quand les utiliser, ainsi que leur intérêt.

Si vous avez déjà touché à clojure ou à arc, vous aurez sûrement apprécié leurs raccourcis pour écrire des fonctions anonymes. Par exemple en Clojure, les deux s-exps suivantes sont équivalentes:

(map (fn [x] (println "Hello, ")) '("world!" "you!"))
(map #(println "Hello, " %) '("world!" "you!"))

Avec arc, c'est le même principe (ça sera [prn "Hello, " _]), sauf qu'on a pas la possiblité d'utiliser ce mécanisme avec plusieurs arguments, tandis qu'avec clojure il est possible d'utiliser %1, %2 pour indiquer le premier argument, le second argument, …

Comme cette fonctionnalitée n'est pas présente dans Common Lisp, nous allons donc l'ajouter grâce aux macros.

Table of Contents

  • 1 Principe
  • 2 Première version simplifiée
  • 3 Une version plus correcte
  • 4 Finalisation
  • 5 Ajout des crochets
  • 6 Conclusion

1 Principe

La première chose à faire avant d'écrire une macro est d'écrire un ou deux exemples d'utilisation de cette macro, afin de clarifier nos idées. Comme la syntaxe '#(…)' est déjà utilisée pour les vecteurs, nous allons utiliser des crochets pour délimiter les fonction anonymes raccourcies, comme dans arc, mais nous allons garder le symbole pourcent suivit d'un nombre de clojure pour identifier les paramètres. Ainsi, l'exemple précédent s'écrira comme suit en Common Lisp grâce à notre macro:

(mapcar [format t "Hello, ~a~%" %] '("world!" "you!"))

Et sera équivalent à:

(mapcar (lambda (x) (format t "Hello, ~a~%" x)) '("world!" "you!"))

Mais premièrement, on va laisser les crochets de côté et écrire une macros qui sera équivalente. On verra à la fin de cet article comment faire pour manipuler le code avant le reader et donc pouvoir utiliser les crochets. Contentons nous pour le moment de manipuler le code après le reader et avant l'évaluation.

2 Première version simplifiée

Dans le développement de macros, il est préférable de coder la macros en plusieurs étapes. Nous allons donc écrire une première version de fn (notre macro), qui ne prendra pas en compte le nombre et l'ordre de ses arguments (le premier qui apparaîtra dans le code sera le premier argument, peu importe son numéro, et (fn (1+ %3)) ne prendra qu'un argument plutôt que trois).

Pour récupérer tous les symboles commençant par un % au sein de notre macro, nous auront besoin des fonctions flatten (voir On Lisp, p. 49) et percent-symbol-p. La première, très utile pour les macros, « aplatit » une liste, par exemple:

> (flatten '(1 (2 (3 4) 5) 6))
(1 2 3 4 5 6)

La seconde permet simplement de savoir si un symbole commence par un %:

> (percent-symbol-p '%foo)
T
> (percent-symbol-p 'foo)
NIL

Voici leur définitions:

(defun flatten (x)
  (labels ((rec (x acc)
             (cond ((null x) acc)
                   ((atom x) (cons x acc))
                   (t (rec
                       (car x)
                       (rec (cdr x) acc))))))
    (rec x nil)))

(defun percent-symbol-p (sym)
  (string= (subseq (symbol-name sym) 0 1)
           "%"))

On peut alors définir fn assez simplement: on récupère tous les symboles commençant par %, et on encapsule le code dans un lambda qui prend les symboles récupérés comme arguments:

(defmacro fn (code)
  (let ((args
         (remove-duplicates
          (remove-if-not #'percent-symbol-p
                         (flatten code)))))
    `(lambda ,args
       ,code)))

Il est donc déjà possible d'utiliser cette macro:

> (mapcar (fn (1+ %)) '(1 2 3))
(2 3 4)

3 Une version plus correcte

Maintenant que l'on a une macro de base qui fonctionne, on peut commencer à régler ses défauts petit à petit. La façon la plus simple pour gérer l'ordre des symboles et prendre en argument des symboles qui n'apparaissent pas dans le code et de récupérer l'argument maximal et de générer la liste de tous les arguments inférieurs.

On va donc avoir besoin de quelques fonctions supplémentaires:

  • mkstr (On Lisp, p. 58) qui transforme tous ses arguments en string
  • symb (On Lisp, p. 58) qui convertit la chaîne de caracère formé par ses arguments en symbol
  • percent-value qui permet de connaître la valeur d'un symbole commençant par % (par exemple, % et %1 valent 1 et %3 vaut 3)
  • gen-percents-syms qui génère tous les symboles pourcents entre 1 et la valeur maximale
(defun mkstr (&rest args)
  (with-output-to-string (s)
    (dolist (a args) (princ a s))))

(defun symb (&rest args)
  (values (intern (apply #'mkstr args))))

(defun percent-value (sym)
  (if (symbolp sym)
    (let ((sym-name (symbol-name sym)))
      (handler-case
          (if (string= (subseq sym-name 0 1) "%")
              (if (= (length sym-name) 1)
                  1
                  (parse-integer (subseq sym-name 1)))
              0)
        (parse-error () 0)))
    0))

(defun gen-percents-syms (max)
  (loop for i from 1 to max
       collect (symb "%" i)))

La nouvelle macro se définit alors:

(defmacro fn (code)
  (let ((arg-max (loop for sym in (flatten code)
                      maximize (percent-value sym))))
    `(lambda ,(gen-percents-syms arg-max)
         ,code)))

4 Finalisation

Notre macro est presque complète, il ne nous reste plus que deux cas à gérer:

  • clojure permet aussi d'utiliser %& pour définir des arguments de type &rest
  • il faut que % soit équivalent à %1

Cela se fait assez facilement:

(defun replace-symbol (symbol1 symbol2 list)
  (typecase list
    (list (mapcar (lambda (x) (replace-symbol symbol1 symbol2 x))
                  list))
    (t (if (eq list symbol1)
           symbol2
           list))))

(defun gen-percents-and-rest (max restp)
  (if restp
    (append (gen-percents-syms max) '(&rest %&))
    (gen-percents-syms max)))

(defmacro fn (code)
  (let ((arg-max (loop for sym in (flatten code)
                      maximize (percent-value sym)))
        (restp (find '%& (flatten code))))
    `(lambda ,(gen-percents-and-rest arg-max restp)
       ,(replace-symbol '% '%1 code))))

5 Ajout des crochets

Nous allons maintenant avoir recours aux read-macros pour modifier le code avant que le reader ne le lise. Tout ce que notre read-macro a à faire ici, c'est de transformer une expression du type [...] en (fn (...)). On définit donc une fonction qui sera appelée chaque fois qu'un crochet ouvrant est rencontré dans le code, qui se charge de lire le code jusqu'au crochet fermant et de retourner le code lu. Le premier argument d'une telle fonction est le flux depuis lequel lire le code, et le second correspond au caractère qui a déclenché son appel. Comme nous ne l'utilisons que pour un [, on peut ignorer ce second argument.

(defun bracket-reader (stream c)
  (declare (ignore c))
  (let (chars)
    (do ((char (read-char stream) (read-char stream)))
        ((char= char #\]))
      (push char chars))
    (read-from-string
     (format nil "(fn (~a))" (coerce (nreverse chars) 'string))))

Et on définit finalement [ comme macro-character:

(set-macro-character #\[ #'bracket-reader)

6 Conclusion

Voilà, au travers de cet exemple j'espère avoir permis à certaines personnes de mieux comprendre l'intérêt des macros et en quoi elles sont si pratique. Le code final pour cette macro est disponible ici

Notez que les read-macros ne sont pas tellement utilisées en pratiques (on aurait très bien pu se limiter à la forme (fn (…)), qui n'est pas tellement plus longue à taper). Néanmoins elles peuvent être assez utiles, par exemple dans CLSQL, où elles permettent de distinguer le code Lisp et les requêtes SQL.

Si vous souhaitez lire plus à propos des macros, je vous recommande la lecture de On Lisp de Paul Graham et de Let Over Lambda de Doug Hoyte.

Article original écrit le 14 mai 2010

Author: Quentin Stievenart <>

Date: 2010-09-17 23:19:41 CEST

HTML generated by org-mode 6.33x in emacs 23

Mieux gérer ses dépôts mercurial

UP | HOME

Mieux gérer ses dépôts mercurial

Vous connaissez sûrement github et bitbucket, c'est très pratique pour hoster des projets qui sont gérés avec git ou mercurial. Mais peut-être que vous préférer hoster ça chez vous, parce que c'est plus marrant, plus souple, tout ça.

Pour ce qui est des projets qui sont gérés avec git, on peut utiliser gitosis (voir ce billet pour plus d'informations sur son installation et son utilisation) avec un cgit/gitweb, ou carrément récupérer le code source de gitorious (ici) et se référer au README.

Le billet n'a pas pour but de parler de ces méthodes, vu qu'il existe sufissament d'informations à propos de celles-ci. Nous allons plutôt voir trois méthodes pour faire de même avec Mercurial.

Table of Contents

  • 1 hg-git
  • 2 hgwebdir et l'authentification http
  • 3 hg-ssh

1 hg-git

La première, assez évidente, consiste à convertir le dépôt mercurial en un dépôt git, et simplement utiliser n'importe quelle méthode citée ci dessus pour gérer le dépôt git.

Pour cela, il faut utiliser hg-git qui vous permettra de garder votre dépôt mercurial, et de pusher/puller comme si c'était git. Les instructions sur le site de hg-git sont assez claires je pense et je ne vais pas les répéter ici.

hg-git nous permet aussi de convertir nos anciens dépôts git en dépôts mercurial si on le souhaite (il suffit de cloner le git), ce qui peut être assez pratique si souhaitez vous rattraper après avoir utiliser git pendant tout un temps.

2 hgwebdir et l'authentification http

Ici on va plutôt avoir recours à notre serveur web pour gérer l'authentification, et ça va nous permettre d'autoriser le push via le port 80, ce qui peut être pratique quand les autres ports sont bloqués.

La configuration se fait donc au niveau de votre serveur web, avec par exemple modauth pour lighttpd et authbasic pour nginx.

Ainsi, la conf lighttpd ressemblera à ceci:

$HTTP["host"] == "hg.foo.bar" {
    server.document-root = "/usr/local/www/foo/hg"
    server.name = "%3%0"

    cgi.assign = ( ".cgi" => "/usr/local/bin/python" )
    url.rewrite-once = ("^(/hgwebdir.cgi/.*)$" => "$1", "^(/.*)$" => "/hgwebdir.cgi$1" )

    auth.backend = "htpasswd"
    auth.backend.htpasswd.userfile = "/usr/local/etc/hg.passwd"

    $HTTP["querystring"] =~ "cmd=unbundle" {
        auth.require = (   "" => (
            "method"  => "basic",
            "realm"   => "mercurial",
            "require" => "valid-user"
            )   
        )   
    }   
}

Pour modifier les options des repos, vous pouvez tout faire dans le fichier .hg/hgrc du repo. C'est par exemple là que vous pouvez indiquer qui aura le droit de push dans ce repo:

[web]
contact = foo
description = foo's repository
allow_push = foo, bar

Il vous reste alors ajouter vos repos dans la conf de hgwebdir.

3 hg-ssh

Si par contre on peut se passer du push via le port 80, il est possible d'imiter le comportement de gitosis avec hg-ssh, un script fourni avec mercurial (dans /usr/local/share/mercurial/contrib/hg-ssh sous FreeBSD, disponible ici si vous ne l'avez pas).

Ce script, combiné avec une bonne configuration du ~/.ssh/authorized_keys, vous permet de définir quel utilisateur peut pusher où, avec une authentification basée sur les clés ssh. C'est donc exactement le même principe que gitosis.

Il faut d'abord créer un utilisateur hg, qui ne va servir qu'à gérer nos repo avec hg-ssh. Il faut aussi que le script hg-ssh soit dans le path, si ce n'est pas le cas, placez le par exemple dans /home/hg/bin/ et ajoutez ce répertoire dans le path de l'utilisateur hg.

Pour permettre à un utilisateur de pusher sur un ou plusieurs repos on peut donc ajouter la ligne suivante dans le ~/.ssh/authorized_keys de l'user hg:

command="hg-ssh repo1 repo2",no-port-forwarding,no-X11-forwarding,no-agent-forwarding <la clé ssh de l'utilisateur>

Et comme on est des grosses feignasses et qu'on veut pas avoir besoin de gérer ce fichier à la main, rien de tel qu'un petit script qui fait le tout à notre place.

Par exemple, pour ajouter l'utilisateur foo et le repo bar, en écriture par foo, il suffit de faire:

% hg-ssh.sh add foo bar
% hg-ssh.sh add_key foo `cat foo.pub`
% hg-ssh.sh dump > ~/.ssh/authorized_keys

Vous pouvez aussi supprimer des utilisateurs, modifier les droits des utilisateurs sur les repos etc. Je pense que l'aide est assez complète pour cela:

% hg-ssh help
hg-ssh.sh command args
commands:
  list                  lists all the users and their repos
  add user [repos]      give write-access to the repos for user, create the 
                        user or the repos if needed
  dump                  dump the new authorized_keys file
  add_key user ssh-key  add the key to the user's keys
  del_user user         delete an user
  del_repos repos       remove repositories from hard drive and configuration
  del_write user repo   remove the write access of one user to a repo
  help:                 print this help screen

Une fois le tout bien configuré, vous pourrez pusher via ssh, même en n'ayant pas de compte sur le serveur:

% hg push ssh://hg@hg.foo.bar/repo

Pour rendre les repos visibles de l'extérieur, vous devrez utiliser hgwebdir, comme vu plus haut, et la conf au niveau des infos sur les repos se fera aussi dans le .hg/hgrc des repos dans le home de l'utilisateur hg.

Voilà donc trois façons de mieux gérer vos repos avec mercurial. Je n'ai pas vraiment été dans les détails pour ce billet, donc si vous avez des questions n'hésitez pas.

Article original écrit le 25 avril 2010

Author: Quentin Stievenart <>

Date: 2010-09-17 11:53:34 CEST

HTML generated by org-mode 6.33x in emacs 23

Découverte de Tinyscheme

UP | HOME

Découverte de Tinyscheme

Tinyscheme est une implémentation de Scheme qui implémente la plupart des fonctionnalitées du R5RS facilement embarquable dans des programmes en C, au même titre que Guile qui, lui, est beaucoup plus complet. Néanmoins on ne souhaite pas spécialement avoir toutes les fonctionnalitées que propose Guile et on peut souvent se limiter à Tinyscheme, qui est plus simple à utiliser.

Un autre avantage de Tinyscheme par rapport à Guile est qu'on peut simplement inclure Tinyscheme avec son programme plutôt que de l'ajouter en dépendance et devoir l'installer soi-même sur son système d'exploitation (il n'est notamment pas dans les ports FreeBSD)

Table of Contents

  • 1 Installation
  • 2 Initialisation
  • 3 Évaluer du code scheme
  • 4 Manipuler des données
  • 5 Définir des fonctions
  • 6 Conclusion

1 Installation

Vous pouvez installer Tinyscheme via votre gestionnaire de paquet ou bien plus simplement le placer dans un sous dossier de votre projet par exemple. Vous pourrez alors linker le tout facilement grâce au paramètre -L de gcc:

gcc foo.c -o foo -Ltinyscheme-1.39/

Parcourez aussi les fichiers scheme.h et scheme-private.h (que vous incluerez bien évidemment dans chaque fichier utilisant tinyscheme), ils vous en apprendront bien plus sur tinyscheme que ce que billet vous apprendra.

2 Initialisation

Avant toute chose, il faut connaître les deux types utilisés par Tinyscheme:

  • un pointer représente un « objet » Scheme, c'est-à-dire n'importe quelle valeur manipulable en Scheme ;
  • un scheme représente l'environnement Scheme dans son état actuel.

L'initialisation de Tinyscheme se fait via la fonction scheme_init_new, qui retourne un nouveau scheme * que l'on passera par la suite en premier argument à toute fonction de Tinyscheme. Pour finaliser cette initialisation, il faut aussi charger le fichier init.scm qui définit une grosse partie du langage (en effet, il est beaucoup plus simple d'implémenter Scheme en Scheme qu'en C). Pour charger ce fichier on passe par scheme_load_file. Il est aussi utile de définir stdout comme port de sortie en passant par la fonction scheme_set_output_port.

On peut donc définir une fonction initialize_scheme qui fera tout ça pour nous:

scheme *
initialize_scheme()
{
        FILE *init;
        scheme *sc;

        sc = scheme_init_new();
        if ((init = fopen("init.scm", "r")) == NULL) {
                perror("fopen");
                return NULL;
        }
        scheme_load_file(sc, init);
        fclose(init);

        scheme_set_output_port_file(sc, stdout);
        return sc;
}

Il faudra aussi appeler scheme_deinit à la fin de notre programme pour arrêter proprement Tinyscheme.

3 Évaluer du code scheme

Pour charger un fichier Scheme il suffit d'utiliser la fonction scheme_load_file de la même façon qu'on l'a utilisée pour charger init.scm Pour directement évaluer du code Scheme depuis le C on utilisera scheme_load_string:

scheme_load_string(sc, "(display \"Hello, world!\") (newline)");

4 Manipuler des données

Il est très simple de créer des objets Scheme depuis le code C. Pour cela, regarder les fonctions du type mk_* dans scheme.h. On peut aussi définir des variables via scheme_define, une variable étant caractérisée par:

  • sa portée
  • son nom (un symbole)
  • sa valeur

Ainsi, pour faire (define foo 42):

pointer value = mk_integer(sc, 42);
scheme_define(sc, sc->global_env, mk_symbol(sc, "foo"), value);

5 Définir des fonctions

Une fonction se définit comme une variable en Scheme, par exemple:

(define 1+
  (lambda (x)
    (+ 1 x)))

On utilisera donc toujours scheme_define pour définir une fonction et il suffit d'utiliser mk_foreign_func pour récupérer un pointer à partir d'une fonction définie en C. Cette fonction doit retourner un pointer et prendre en argument un scheme * représentant l'état actuel de l'interpréteur ainsi qu'un pointer qui contient les arguments passés à cette fonction.

Grâce à cela, on peut donc ajouter de nouvelles fonctions qu'il serait impossibles de définir en Scheme, par exemple getenv (redéfinir 1+ en C n'aurait aucun intérêt vu la simplicité de cette définition en Scheme) :

pointer
scm_getenv(scheme *sc, pointer args)
{
        char *value;
        if (args == sc->NIL 
              || !is_string(pair_car(args))
              || (value = getenv(string_value(pair_car(args)))) == NULL)
                return sc->F;
        return mk_string(sc, value);
}

La valeur sc->NIL est assez explicite, et sc->F représente le booléen #f (sc->T représente quant à lui #t). Il faut bien évidemment informer Tinyscheme de l'existence de cette fonction:

scheme_define(sc, sc->global_env,
              mk_symbol(sc, "getenv"), mk_foreign_func(sc, scm_getenv))

6 Conclusion

Ce qui a été présenté ici couvre − je pense − la plupart des fonctionnalitées de Tinyscheme utilisées par des programmes tels que Tiny-Fu, le système de script-fu de GIMP.

Il est donc très facile d'embarquer Tinyscheme dans vos applications, et il est aussi simple d'écrire des extensions pour Tinyscheme (il suffira de définir un ensemble de fonctions et de compiler le tout en -shared). Pour des exemples concrets d'extensions, regardez TSX ainsi que l'extension regex.

La documentation de Tinyscheme étant inexistante (excepté les fichiers Manual.txt et hack.txt inclus avec Tinyscheme) si vous souhaitez plus d'exemples, il est essentiel de se plonger dans les sources de Tiny-Fu, de TSX ainsi que d'autres logiciels utilisant Tinyscheme si nécessaire.

Article original écrit le 20 mars 2010

Author: Quentin Stievenart <>

Date: 2010-09-17 11:54:42 CEST

HTML generated by org-mode 6.33x in emacs 23

FreeBSD ports on steroids - part 1

UP | HOME

FreeBSD ports on steroids - part 1

Voici le premier billet d'une série sur les ports FreeBSD. Au sommaire aujourd'hui: l'amélioration des performances des ports avec fastest_sites, ccache et cpuflags.

Table of Contents

  • 1 fastest_sites: améliorer le téléchargement des fichiers
  • 2 ccache: améliorer la compilation des ports
  • 3 cpuflags: améliorer l'éxécution des ports

1 fastest_sites: améliorer le téléchargement des fichiers

fastest_sites est un petit script python qui trie les MASTER_SITE en fonction de leur temps de réponse, ce qui permet de redéfinir l'ordre d'utilisation des mirroirs pour utiliser les plus rapides en priorité. Comme il doit se connecter à chaque mirroir, son éxécution prend un certain temps, n'hésitez pas à continuer la lecture de cet article pendant ce temps.

cd /usr/ports/ports-mgmt/fastest_sites
make install clean
fastest_sites > /usr/local/etc/ports_sites.conf
echo '.include "/usr/locale/etc/ports_sites.conf"' >> /etc/make.conf

2 ccache: améliorer la compilation des ports

ccache, comme son nom l'indique, garde en cache les compilations de programmes C ou C++, et permet donc de ne pas recompiler quelque chose qui a déjà été compilé auparavant ce qui améliore grandement le temps de compilation.

Il est disponible dans le port devel/ccache. Une fois installé, on lit le fichier /usr/local/share/doc/ccache/ccache-howto-freebsd.txt comme indiqué. Dans mon cas, il faut donc ajouter ceci à /etc/make.conf:

.if (!empty(.CURDIR:M/usr/src*) || !empty(.CURDIR:M/usr/obj*)) && !defined(NOCCACHE)
  CC=/usr/local/libexec/ccache/world-cc
  CXX=/usr/local/libexec/ccache/world-c++
.endif

ainsi que ceci dans le /etc/profile (attention, j'utilise mksh comme shell root mais le shell par défaut pour est un csh, à adapter donc):

export PATH=/usr/local/libexec/ccache:$PATH
export CCACHE_PATH=/usr/bin:/usr/local/bin

On peut aussi adapter CCACHE_DIR qui contient le dossier où ccache stockera ses données (/root/.ccache/ par défaut), et CCACHE_LOGFILE qui contient le chemin vers le fichier de log. On peut finalement adapter la taille maximale du cache via l'option -M:

# ccache -M 5G
Set cache size limit to 5242880k

3 cpuflags: améliorer l'éxécution des ports

cpuflags est un script shell qui vous donne les CFLAGS adaptés à votre processeur. Cepandant il n'est pas encore disponible dans les ports FreeBSD, bien qu'il soit compatible avec FreeBSD. Néanmoins, il est disponible dans pkgsrc, voici donc une démarche plus ou moins propre pour l'installer via les ports1.

Tout d'abord, on récupère les fichiers le concernant depuis le cvs de pkgsrc:

export CVSROOT=anoncvs@anoncvs.de.netbsd.org:/cvsroot
export CVS_RSH=ssh
cvs checkout -PA pkgsrc/devel/cpuflags

Il faut ensuite convertir ce pkgsrc en un port, pour pouvoir l'installer via les ports:

cd pkgsrc/devel/cpuflags
mv PLIST pkg-plist
mv DESCR pkg-descr
wget http://awesom.eu/~acieroid/files/ports/cpuflags/Makefile.diff
patch -p1 Makefile < Makefile.diff
rm Makefile.diff
make install clean

Il suffit ensuite d'ajouter ce que nous sort cpuflags aux CFLAGS, dans le make.conf:

echo "CFLAGS+=`cpuflags`" >> /etc/make.conf

Article original écrit le 28 février 2010

Footnotes:

1 Peu après cet article, j'ai soumis le port ainsi créé, et il finalement été accepté. Vous pouvez donc passer toute cette démarche et simplement installer le port devel/cpuflags

Author: Quentin Stievenart <>

Date: 2010-09-17 11:53:07 CEST

HTML generated by org-mode 6.33x in emacs 23

Bépo et DragonFly BSD

UP | HOME

Bépo et DragonFly BSD

La disposition de clavier bépo est disponible pour FreeBSD, mais elle n'est pas compatible avec DragonFly BSD, bien que cette dernière soit dérivée de FreeBSD. C'est en fait dû à kbdcontrol(1) qui n'est pas compatible à 100% entre FreeBSD et DragonFly. Les développeurs de FreeBSD ont continués à ajouter des nouvelles fonctionnalitées après le fork de DragonFly, qui n'ont pas été reprises chez DragonFly.

Néanmoins il reste possible de « convertir » le pilote bépo de FreeBSD dans quelque chose de compatible avec le kbdcontrol de DragonFly BSD. La seule incompatibilitée utilisée dans le driver bépo est l'utilisation de l'action paste, qui a été ajoutée dans FreeBSD 5.0. Un petit coup de sed pour réparer ça, et on peut installer et charger le driver:

wget http://download.tuxfamily.org/dvorak/devel/fr-dvorak-bepo-kbdmap-1.0rc2.tgz
tar xzvf fr-dvorak-bepo-kbdmap-1.0rc2.tgz
cd fr-dvorak-bepo-bkdmap-1.0rc2
sed -i -e 's/paste/nop/g' fr-dvorak-bepo.kbd
cp fr-dvorak-bepo.kbd /usr/share/syscons/keymaps/

On peut maintenant switcher au bépo:

kbdcontrol -l fr-dvorak-bepo.kbd

On modifie ensuite /etc/rc.conf pour avoir le bépo au démarrage:

keymap="fr-dvorak-bepo"

Par contre, même remarque que sur le wiki bépo, la console de FreeBSD ne supporte pas l'UTF-8, c'est donc aussi le cas de DragonFly BSD.

(Article original écrit le 13 février 2010)

Author: Quentin Stievenart <>

Date: 2010-09-17 11:52:32 CEST

HTML generated by org-mode 6.33x in emacs 23

Vole libellule, vole !

UP | HOME

Vole libellule, vole !

J'ai enfin pu m'installer un BSD utilisable sur mon desktop ! Ça faisait longtemps que je le voulais, mais le problème venait de la carte wifi, sans drivers sous BSD (elle passait sous FreeBSD avec ndisgen, mais je préfère éviter ça). J'ai donc fait l'acquisition d'une nouvelle carte wifi censée être supportée par rum(4), et à part quelques problèmes, désormais résolus, ça fonctionne à merveille.

J'ai d'abord regardé du côté de NetBSD, qui fonctionnait plutôt bien excepté pour le wifi. J'obtenais par moment 50% voire 75% de paquets perdus lors d'un ping(8). Je me suis alors tourné vers DragonFly BSD, ceux qui ajouté le driver bwi(4) qui a ensuite été repris par OpenBSD (en 4.3), puis par FreeBSD (en 8.0) et il est toujours en -current chez NetBSD et devrait être inclut dans NetBSD 6.0.

Table of Contents

  • 1 USB
  • 2 Wifi
  • 3 Configuration
    • 3.1 Bépo
    • 3.2 La souris en tty
    • 3.3 Mise en veille
    • 3.4 UTF-8
    • 3.5 Désactiver le beep système
    • 3.6 Moins de tty
    • 3.7 Désactiver les messages du kernel (silent boot)
  • 4 Carte son
  • 5 Logiciels utilisés
  • 6 To be continued…

1 USB

Après l'installation, l'USB n'était pas détecté par l'OS à cause du support Plug And Play du BIOS, il suffit donc le désactiver cette option ainsi que le support USB (sinon on se retrouve avec **énormément** d'interruptions, ce qui rends le système peu utilisable).

On peut se rendre compte de la non-détection des périphériques USB via usbdevs(8):

# avec le support PnP et USB activés dans le bios:
$ usbdevs -v
usbdevs: no USB controllers found

Par contre, en désactivant le support de l'USB dans le BIOS, on perd la possibilitée d'utiliser un clavier USB avec GRUB et avec le bootloader de DragonFly BSD.

2 Wifi

J'ai donc acheté une carte wifi USB pour pouvoir me passer de mes cartes Broadcom (BCM34181 sur le desktop et BCM4312 sur le netbook), même si la BCM4318 est supportée par bwi(4) (le driver fonctionne, mais est inutilisable, les vitesses de téléchargement sont de l'ordre du bit par seconde). La nouvelle carte est une TP-Link TL-WN321G, du chipset rum(4), et est détectée directement (à partir du moment où l'USB fonctionne). Un petit coup d'ifconfig(8) et de route(8), et hop, on est connecté. Le driver à l'air de fonctionner assez bien la plupart du temps, même si il lui arrive d'avoir un comportement bizarre (le débit tombe parfois aux alentours des 20kB/s, sans raison apparente)

3 Configuration

Je vais indiquer ici les étapes de la configuration qui ne sont pas mentionnées dans la documentation de DragonFly BSD. Lors d'une première installation, le handbook est très pratique mais il faut savoir chercher par soi même car il n'est pas totalement à jour (par exemple, depuis la version 2.4 DragonFly utilise un /dev dynamique, ce qui n'est mentionné nulle part dans le handbook où ils utilisent le vieux /dev/MAKEDEV pour créer les devices).

3.1 Bépo

Au niveau du bépo dans la console, le driver [pour FreeBSD] ne fonctionne pas. Je n'ai pas encore pris le temps de chercher plus loin de ce côté car dès que le réseau était fonctionnel, j'ai tout configuré via ssh depuis un autre ordinateur pourvu du bépo.

Pour ce qui est du driver dans X, il n'est pas à jour et il faut donc le mettre à jour soi-même (une fois X installé, bien sûr):

# cd /usr/pkg/share/X11/xkb/symbols
# mv fr fr.bak
# fetch -o fr http://www.clavier-dvorak.org/donnees/xkb/fr-bepo.1.0-rc1-hardy 

On ajoute ensuite ceci dans /etc/X11/xorg.conf, à la section InputDevice du clavier:

Option          "XkbRules"      "xorg"
Option          "XkbModel"      "pc105"
Option          "XkbLayout"     "fr"
Option          "XkbVariant"    "bepo"

3.2 La souris en tty

Ce n'est certainement pas la chose la plus utile, mais ça permet de voir si la souris fonctionne bien avant d'avoir installé X. On regarde d'abord sur quel device se trouve la souris (si elle est détectée):

$ grep Mouse /var/log/messages
Jan 31 17:46:51 newton kernel: ums0: <Logitech USB-PS/2 Optical Mouse, class 0/0, rev 2.00/20.00, addr 2> on uhub0

On ajoute ensuite ceci au rc.conf:

moused_enable="YES"
moused_port="/dev/ums0"

Et on peut démarrer moused:

# rcstart moused

3.3 Mise en veille

Pour la mise en veille, il faut charger le module acpi.ko s'il n'est pas chargé par défaut:

# kldload acpi.ko

Ensuite, on utilise acpiconf(8) pour mettre l'ordinateur en veille, par exemple pour faire l'équivalent d'un s2ram:

# acpiconf -s 3

Hélas, le seul mode d'hibernation qui fonctionne sur mon ordinateur est le 1 (qui arrête juste l'horloge du CPU).

3.4 UTF-8

Pour avoir l'UTF-8, il suffit de rajouter ceci de modifier le /etc/login.conf pour que la fin de la section default: ressemble à ceci:

:ignoretime@:\
:umask=022:\
:charset=UTF-8:\
:lang=en_US.UTF-8:

Le changement sera donc fait pour tout le système et cela nous évite d'avoir à modifier nous-même les variables LC_* et LANG, ce qui peut varier selons les shells utilisés.

3.5 Désactiver le beep système

On peut désactiver ce beep à deux niveaux: dans le tty ou dans X:

# dans le tty
kbdcontrol -b off
# dans X
xset -b

La première commande est à placer dans /etc/profile et la seconde dans ~/.xinitrc si vous utilisez startx.

3.6 Moins de tty

Je n'ai jamais vraiment compris l'utilité d'avoir 8 tty activés par défaut alors qu'un suffit. On va donc tous les désactiver sauf les deux premiers (au cas où on oublie de lancer tmux ou screen avant une commande qui met du temps à s'éxécuter). Pour cela, il suffit de commenter les lignes commencant par ttyvn ou n va de 3 à 8 dans le fichier /etc/tty.

3.7 Désactiver les messages du kernel (silent boot)

C'est très désagréable de se faire déranger par les messages du kernel quand on travaille en tty. Ça se désactive facilement en mettant l'option kern.consmute de sysctl(8) à 1. Cela peut se faire au niveau de /etc/sysctl.conf, qui est chargé après le chargement du kernel, donc vous verrez toujours les messages pendant le boot. Pour avoir un silent boot, c'est-à-dire un boot sans messages du kernel, il suffit simplement de changer cette option au niveau du fichier /boot/loader.conf.

La ligne à ajouter dans les deux cas est:

kern.consmute="1"

4 Carte son

Ma carte son étant une ATI IXP, il a suffit de charger le module snd_atiixp.ko et de l'ajouter au rc.conf:

# kldload snd_atiixp.ko
# echo 'snd_atiixp_load="YES"' >> /etc/rc.conf

Et pour tester le son, rien de tel qu'un petit

# cat /dev/urandom > /dev/audio

5 Logiciels utilisés

Pour finir, voici les principaux logiciels que j'utilise et le nom de leur pkgsrc:

UtilitéLogicielPort
Window Managerwmiiwm/wmii
Terminalrxvt-unicodex11/rxvt-unicode
Multiplexeur de terminauxtmuxmisc/tmux
Éditeur de texte2editors/vim, editors/emacs-snapshot
Navigateur3www/firefox
Lecteur audiompd et mpcaudio/musicpd et audio/mpc
Pagermostmisc/most
Shellmkshshells/mksh
Lecteur PDF4print/xpdf
Visionneur d'imagesxvgraphics/xv

6 To be continued…

Il reste encore beaucoup de choses à regarder de plus près (monter de l'ext4 et du reiserfs, le bépo dans la console, …), dont certaines propres à DragonFly BSD (hammer, vkernel). Cela fera peut-être l'objet de futurs billets.

J'aimerais aussi me pencher sur la création de pkgsrc, notamment pour installer proprement mupdf et pouvoir me passer de xpdf.

Article original écrit le 2 février 2010

Footnotes:

1 Notez que depuis FreeBSD 8.1, il existe le driver bwn (qui n'existait pas à l'écriture de cet article) qui supporte parfaitement cette carte.

2 Vim pour configurer et Emacs pour programmer.

3 Avec le plugin vimperator.

4 Regardez du côté de mupdf pour un autre lecteur intéressant (mais pas (encore) dans pkgsrc).

Author: Quentin Stievenart <>

Date: 2010-09-17 23:20:26 CEST

HTML generated by org-mode 6.33x in emacs 23

Des tips, enfin !

UP | HOME

Des tips, enfin !

Ça fait un moment qu'on voulait un système de tips sur awesom qui nous permettrait d'avoir un endroit ou stocker des simples astuces, et qui pourrait par la même occasion être accessible à d'autres personnes.

On a tout d'abord pensé à un StatusNet, mais c'était peu adapté à ce qu'on voulait (mais pour une utilisation « normale », ça poutre). On a donc décidé qu'on le coderait nous même, ce qu'Izu a commencé avec web.py, et s'est arrêté une fois que ça affichait les tips, mais on n'avait pas de possibilitée d'en ajouter et quelques problèmes à le mettre en place proprement avec lighttpd. De mon côté, j'ai essayé dancer et nitrogen, mais c'était un peu trop chiant à utiliser pour faire un truc aussi simple que tips. Izu a ensuite fait quelque chose de pas mal avec Code Igniter, mais toujours sans la possibilitée d'ajouter des tips. J'ai finalement testé Arc et ça m'a semblé être un bon choix.

Table of Contents

  • 1 Arc, un langage orienté web
  • 2 Installation d'Arc
  • 3 Mise en place sur awesom

1 Arc, un langage orienté web

Arc est un langage de la famille des lisp, conçu par Paul Graham, un hacker Lisp assez renommé qui écrit beaucoup, aussi bien des essais que des bons livres sur le Common Lisp.

La particularité de Arc est qu'il est incroyablement bien adapté pour développer des applications web avec ou même n'importe quelle application qui utilise le réseau1 (la gestion des sockets est simple avec Arc contrairement aux sockets de Common Lisp qui s'utilisent de façon différente sur chaque implémentation, même s'il existe des bibliothèques plus ou moins portables). Arc a beau être léger, il implémente énormément de fonctions très utiles pour le développement web, ce qui permet de se concentrer sur l'application en soi plutôt que de coder encore et encore les même fonctions qui manquent dans le langage.

Évidemment, Arc étant un dérivé de Lisp, vous pouvez toujours définir des macros (lisez les chapitres 7 et 8 de Practical Common Lisp si vous débutez en Common Lisp et que vous ne conaissez pas les macros, pour ceux qui ont un niveau plus élevé, regardez du côté de On Lisp ou même de Let Over Lambda), ce qui simplifie grandement le développement.

Je ne vais pas parler plus en détail de Arc, si vous voulez plus d'informations, je vous recommande Design Philosophy (ou en français) et Why Arc Isn't Especially Object-Oriented qui peuvent vous donner une première impression sur la façon dont Arc est conçu. Pour avoir un aperçu d'Arc, regardez aussi Take the Arc Challenge et comparez le avec les solutions dans d'autres langages.

2 Installation d'Arc

Plusieurs implémentations d'Arc sont disponibles, dont les deux principales, implémentées en PLT-Scheme:

  • tout d'abord, la version « officielle » de Paul Graham, qui date un peu ;
  • ensuite, Anarki une version dérivée de la version officielle, qui est bien plus à jour et peut-être légèrement incompatible avec celle de Graham.

Il existe aussi d'autres versions moins actives, moins utilisées et surtout moins complètes, mais qui valent quand même le coup d'œil:

  • un compilateur Arc vers C, arc2c ;
  • une implémentation en Java, rainbow ;
  • une implémentation en Javascript, arclite ;
  • une implémentation en .NET avec MBase ;
  • une implémentation en Common Lisp avec SBCL, arc-sbcl.

Personnellement j'utilise Anarki, qui est de loin la version la plus complète et active. Pour l'installation, un coup de

git clone http://github.com/nex3/arc
ln -s arc/arc.sh ~/bin

et c'est parti pour de folles heures de hack. N'oubliez pas d'installer les fichiers relatif à votre éditeur de texte, disponibles dans extras (il y a un arc-mode et un inferior-arc-mode pour GNU Emacs ainsi que les fichiers d'indentation, de syntaxe et une extension pour intégrer la REPL d'Arc à Vim)

Pour commencer à programmer avec Arc, lisez d'abord l'introduction de Paul Graham et utilisez la documentation disponible sur l'Arc Language Blog, qui est quasi complète, mais pas totalement, il vous restera donc à trouver votre documentation en lisant les sources d'Arc. Parcourez aussi les deux exemples d'applications disponibles avec Arc: un simple blog et le code source de Hacker News. N'hésitez pas non plus à jeter un œil au code source de tips (qui s'inspire de ces deux exemples).

J'ai aussi mis en place une [page](http://awesom.eu/~acieroid/arc) regroupant les ressources francophones disponibles pour Arc, toute contribution est la bienvenue2.

3 Mise en place sur awesom

Arc a été installé dans une jail et il a simplement suffit de récupérer tips dessus (via son dépôt mercurial (que je commence d'ailleurs à préférer à git, car plus simple et plus logique tout en restant puissant)), il ne restait donc plus qu'à configurer le serveur web (en l'occurrence, lighttpd).

Ce dernier point a été assez dur a mettre en place car le `modproxy` de lighttpd refusait simplement de marcher avec Arc (la seule réponse qu'on obtenait du serveur était une page blanche). On a donc mis en place un nginx (qui est très simple à configurer soit dit en passant) dans la jail qui écoute sur le port 80 et qui fait proxy vers tips (qui lui tourne sur le 8080).

Arc étant lancé dans une REPL, on peut facilement modifier les paramètres (url, etc.) et surtout, mettre à jour tips sans même devoir couper le service (en rechargeant simplement les fichiers sources).

Voilà, on a enfin quelque chose pour gérer nos tips, sur tips.awesom.eu, il faut juste espérer que ça se remplisse petit à petit.

Article original écrit le 25 janvier 2010

Footnotes:

1 : Lors de l'écriture de cet article, je n'avais pas encore essayé de faire une application autre que web, utilisant le réseau. Chose que j'ai essayé de faire après, et il s'avère qu'arc ne fournit que des fonctions pour agir comme serveur, donc l'écriture d'un client quelconque est impossible avec arc.

2 : page qui n'a jamais vraiment été active sauf lors de l'écriture de ce billet, et qui ne le sera sûrement jamais.

Author: Quentin Stievenart <>

Date: 2010-09-17 11:52:02 CEST

HTML generated by org-mode 6.33x in emacs 23

Arc: philosophie de conception (traduction)

UP | HOME

Arc: philosophie de conception (traduction)

Nous avons été critiqué pour avoir conçu Arc comme un langage pour les bons programmeurs. Un bon design, cela va de soi, signifie comprendre les besoins de vos utilisateurs, et la plupart des programmeurs ne sont pas de bons programmeurs.

Examinons deux voitures: la Cadillac Coupe de Ville de 1973 et la Porsche 911 de 1973. La Cadillac a été conçue soigneusement pour plaire au conducteur moyen. La 911 a été conçue pour la performance. Laquelle des deux est mieux conçue ?

Cadillac-1973.jpg

Cadillac Coupe De Ville de 1973

Porsche-1973.jpg

Porsche 911E de 1973

J'ai délibérément choisis des images de 1973. C'est plus simple de distinguer les bonnes conceptions des mauvaises quand vous ne considérez pas les tendances actuelles. (Jetez un œil aux coupes de cheveux dans un vieil album de promotion de l'université.)

Les bonnes conceptions sont intemporelles, et si vous voulez quelquechose d'intemporel vous ne pouvez pas vous limiter par les besoins d'un hypothétique utilisateur « moyen ». C'est un public trop vague. C'est aussi un public trop changeant: l'utilisateur moyen peut ne pas être aussi stupide que vous le pensez.

Regardez ce qu'il s'est passé avec la Porsche 911. Il est tellement évident qu'elle est clairement supérieure à la Cadillac que même un enfant pourrait dire qu'elle est meilleure. Et, durant les années 1980, même les revendeurs de drogue pour qui la Cadillac était conçue voulaient la 911 à la place.

Il se passe la même chose pour les langages de programmation. Les grands langages sont ceux que les bons programmeurs ont conçu pour le propre utilisation – C, Smalltalk, Lisp. Les langages qui ont consciemment été conçu pour les programmeurs « moyens » (Cobol, Pascal, Ada) ont eu tendance à être des évolutions sans avenirs.

Le seul plan fiable est de concevoir pour la performance. Performance ne veut pas dire vitesse; c'est prendre la métaphore trop litérallement. La vitesse compte, mais un langage de programmation est avant tout un outil en quoi penser. Nous souhaitons que penser en Arc procure la même sensation que conduire une 911.

(Je ne prétends pas que ce sera le cas, juste que c'est le but)

Texte original

Author: Paul Graham <>

Date: 2010-09-17 11:54:48 CEST

HTML generated by org-mode 6.33x in emacs 23

Chronicle: suite et fin

UP | HOME

Chronicle: suite et fin

Après l'écriture de mon premier post, j'ai fait part de mes modifications à Steve Kemp, l'auteur de Chronicle, qui a très vite répondu et a non seulement implémenté le support de la coloration (uniquement via Vim), mais a séparé la partie touchant au formattage des billets de la partie principale. Le formattage se fait donc maintenant grâce à bin/chronicle-entry-filter, qui permet de définir des pré/post filtres, qui seront appelés avant/après le formattage principal du texte (transformation en html et coloration) ce qui permet de pouvoir faire proprement ce que l'on veut au texte, sans devoir modifier le programme.

Le seul petit problème, c'est que la coloration ne s'effectue que sur les balises <code> pourvues d'un attribut lang, et il faut un moyen de spécifier à markdown/textile cet attribut. Pour textile, c'est très simple, il suffit de spécifier le langage entre crochet quand on utilise bc:

bc[perl]. print "Hello, world!";

Avec markdown (ce qui marche aussi avec textile), on peut remédier à ce problème en écrivant directement du code html (donc on fout notre code dans une balise <code> avec un attribut lang). Et si ça vous fait chier d'écrire ça à chaque bout de code, il suffit d'utiliser un pré-filtre qui matche par exemple "highlight:language" au début de chaque bout de code et qui le remplace par le bout de code html.

Bien évidemment, ce système de pré/post filtre ne se limite pas à la coloration syntaxique, mais permet de faire un peu ce qu'on veut avec les billets (l'exemple donné par Steve dans les sources est par exemple d'utiliser un tr [a-z] [A-Z] comme post-filtre :-').

Voilà, maintenant je ne peut pas ne pas vous recommander d'utiliser Chronicle comme moteur de blog, une nouvelle release devrait sortir d'ici quelques jours avec ces fonctionnalitées (et tout est déjà dans le dépôt mercurial).

Article original écrit le 27 décembre 2009

Author: Quentin Stievenart <>

Date: 2010-09-17 11:51:54 CEST

HTML generated by org-mode 6.33x in emacs 23

Chronicle: générateur de blog statique

UP | HOME

Chronicle: générateur de blog statique

Voilà, ce premier billet va donc présenter le moteur de blog utilisé: Chronicle1. En effet, je souhaitait un moteur de blog assez simple et qui génère du contenu statique (comme nanoblogger, utilisé par heaumer), et après de nombreuses recherches je suis resté sur Chronicle qui était le seul, excepté nanoblogger, à générer un blog statique (en plus y a même un script cgi si jamais je veux activer les commentaires).

Le principe est donc simple: vous écrivez vos billets dans un des langages de formattage disponibles, c'est-à-dire Markdown, Textile, ou directement du HTML. Une fois votre article écrit, vous lancez chronicle avec les options kivonbien, et votre blog est généré en html statique, et il ne reste plus qu'à le rendre accessible via n'importe que serveur web.

Néanmoins, il a fallu modifier quelques petites choses pour avoir le fonctionnement voulu. Heureusement c'est du perl et ça se laisse tripoter facilement.

Table of Contents

  • 1 Le thème
  • 2 Coloration syntaxique
  • 3 Autres améliorations possibles

1 Le thème

Les cinq thèmes fournis avec Chronicle ne sont pas parfait, en fait ils sont tous moches, sauf le thème « simple », mais qui a certains défauts (les liens en jaune clair quand on passe dessus, c'est immonde). Je suis donc parti de ce thème que j'ai légèrement modifié. La modification se fait très facilement, quelques valeurs à changer dans les CSS, quelques modifications dans les .templates utilisés pour générer l'html et le travail est fait. Le thème final est disponible ici.

2 Coloration syntaxique

Ensuite vient le problème de la coloration syntaxique, qui est assez indispensable au niveau esthétique pour un blog qui est destiné à parler programmation. Hélas, il n'y a pas de coloration syntaxique dans Chronicle, mais ça peut s'ajouter facilement.

Au niveau du logiciel utilisé pour la coloration, mon choix s'est d'abord porté vers Pygments via XML-RPC (le serveur XML-RPC doit avoir une fonction highlight_code qui prends en premier paramètre le code à colorer et en second paramètre le langage, ce qui est par exemple le cas du script écrit par delroth), mais finalement j'utilise la coloration syntaxique de Vim (via Text::VimColor), que j'avais d'abord éliminé car dans mes souvenirs ça ne convenait pas, ce qui était le cas pour paste.pl, qui devait éxécuter Vim pour colorer le texte (ce qui n'est pas génial niveau sécu pour un script CGI), mais dans ce cas-ci, le script étant éxécuté par l'utilisateur uniquement à la génération, ça ne pose aucun problème.

J'ai aussi ajouté la possibilitée d'utiliser Kate, un autre module de coloration syntaxique directement implémenté en Perl. L'utilisateur peut donc choisir la méthode qu'il préfère en l'indiquant dans le fichier de configuration (paramètre highlight qui est mis soit à kate, vim ou pygments).

Pour ma part, j'utilise donc la coloration avec Vim, car plus de langages sont supportés (520 fichiers dans /usr/local/share/vim/vim72/syntax/), et dans le cas de langages peu utilisés, il y a peu de chances d'avoir une coloration dans Pygments ou avec Kate, tandis que dans Vim, oui (c'est par exemple le cas de arc).

Pour spécifier du code à colorer, il doit commencer par highlight:langage, avec langage qui est un langage supporté par la méthode de coloration que vous avez choisie.

Je mets à votre disposition le diff, pour que vous puissiez patcher Chronicle si jamais vous avez envie d'avoir une coloration syntaxique de dingue, et voilà le résultat:

(defun hello (who)
  (format t "Hello, ~a!~%" who))

3 Autres améliorations possibles

On pourrait aussi ajouter un paramètre qui permettrait à l'utilisateur d'utiliser une commande shell de son choix pour formatter le code (un script python pour utiliser Pygments par exemple, c'est plus simple que d'utiliser XML-RPC), et on pourrait faire de même pour le formattage du texte (pour utiliser mdown ou d'autres logiciels). Je le ferait peut être un jour si l'envie me vient.

Article original écrit le 26 décembre 2009

Footnotes:

1 Notez que ce n'est plus le moteur utilisé maintenant, j'utilise maintenant org-mode avec un peu de Common Lisp pour générer l'index.

Author: Quentin Stievenart <>

Date: 2010-09-17 11:51:21 CEST

HTML generated by org-mode 6.33x in emacs 23