Un écueil majeur est que la sémantique de liaison pour les variables non définies - c'est-à-dire les variables non définies avec defvar
et amis - change avec lexical-binding
: Sans elle, let
lie tout dynamiquement, mais avec les lexical-binding
variables non définies activées sont liées lexicalement , et même élides complètement si inutilisées dans la portée lexicale actuelle .
L'ancien code repose parfois sur cela. Pour éviter les dépendances matérielles pour les fonctionnalités facultatives, il lierait les variables dynamiques sans nécessiter la bibliothèque correspondante ou déclarer la variable elle-même:
(let ((cook-eggs-enabled t))
(cook-my-meal))
Si la fonction de cuisson est facultative, nous ne voulons pas forcer des dépendances inutiles sur l'utilisateur, nous n'utilisons donc pas (require 'cook)
et nous nous appuyons plutôt sur le chargement automatique de la cook-my-meal
fonction.
Il est évident pour le lecteur humain qu'il cook-eggs-enabled
ne s'agit pas d'une variable locale, mais se réfère toujours à une variable dynamique globale de la cook
bibliothèque ici. Sans lexical-binding
ce code fonctionne comme prévu: cook-eggs-enabled
est lié dynamiquement, qu'il soit défini ou non.
Avec lexical-binding
cependant, il se casse: cook-eggs-enabled
est maintenant lié lexicalement (puis optimisé loin, parce qu'il est pas utilisé), de sorte que la variable globale dynamique cook-eggs-enabled
est pas toujours touché du tout et encore nil
par le temps cook-my-meal
est appelé, donc nous étonnamment pas des œufs dans notre repas.
Heureusement, ces problèmes sont très faciles à repérer : le compilateur d'octets vous avertit naturellement d'une liaison lexicale inutilisée ici.
La solution est simple: ajoutez un (require 'cook)
(pour les fonctionnalités qui ne sont pas vraiment facultatives de toute façon) ou, pour éviter les dépendances matérielles, déclarez la variable comme variable dynamique dans votre propre code . Il existe un defvar
formulaire spécial pour cela:
(defvar cook-eggs-enabled)
Cela définit cook-eggs-enabled
comme variable dynamique, mais n'affecte pas la docstring, le load-history
(et donc find-variable
et amis) ou quoi que ce soit d'autre, sauf la nature contraignante de la variable.
cook-eggs-enabled
pas une dissociation à lalet
fin? Je suis presque sûr d'avoir rencontré un bug comme celui-ci auparavant. Le defvar se produisait à l'intérieur dulet
, et lelet
dernier a restauré la variable à son état initial (vide).