Quais são as possíveis armadilhas da ativação da ligação lexical para um buffer?

12

Isto foi inspirado pela discussão de lexical de ligação vs lexical-deixou em esta questão . Como a vinculação lexical permite que você tenha fechamentos úteis com os quais as pessoas podem se acostumar em outros idiomas, como o JavaScript, por que você não o ativava o tempo todo?

Presumir que a compatibilidade com versões anteriores com o Emacsen mais antigo não é uma preocupação, que armadilhas você deve procurar ao habilitá-lo em buffers de código herdados?

stsquad
fonte

Respostas:

13

Uma grande armadilha é que a semântica de ligação para variáveis indefinidas - ou seja, variáveis ​​não definidas com defvare amigos - muda com lexical-binding: Sem ela, letvincula tudo dinamicamente, mas com lexical-bindingvariáveis ​​indefinidas ativadas é vinculada lexicamente e até elidida completamente se não for usada no escopo lexical atual .

Às vezes, o código antigo depende disso. Para evitar dependências rígidas para recursos opcionais, ele vincularia variáveis ​​dinâmicas sem exigir a biblioteca correspondente ou declarar a própria variável:

(let ((cook-eggs-enabled t))
  (cook-my-meal))

Se o recurso de cozimento for opcional, não queremos impor dependências desnecessárias ao usuário; portanto, não usamos (require 'cook)e, em vez disso, dependemos do carregamento automático da cook-my-mealfunção.

É óbvio para o leitor humano que cook-eggs-enablednão é uma variável local, mas ainda se refere a alguma variável dinâmica global da cookbiblioteca aqui. Sem lexical-bindingesse código, funciona como pretendido: cook-eggs-enabledé vinculado dinamicamente, definido ou não.

Com lexical-bindingentanto, ele quebra: cook-eggs-enabledagora é obrigado lexically (e, em seguida, otimizado para longe, porque não é usado), então a variável dinâmica mundial cook-eggs-enabledé não já tocou em tudo e ainda nilno momento em que cook-my-mealé chamado, de modo que, surpreendentemente, não terá quaisquer ovos na nossa refeição.

Felizmente, esses problemas são muito fáceis de identificar : o compilador de bytes naturalmente alerta sobre uma ligação lexical não utilizada aqui.

A correção é simples: adicione um (require 'cook)(para recursos que não são realmente opcionais de qualquer maneira) ou, para evitar dependências graves, declare a variável como variável dinâmica em seu próprio código . Existe um defvarformulário especial para isso:

(defvar cook-eggs-enabled)

Isso define cook-eggs-enabledcomo variável dinâmica, mas não afeta a sequência de caracteres, o load-history(e, portanto, os find-variableamigos) ou qualquer outra coisa, exceto a natureza vinculativa da variável.

lunaryorn
fonte
No caso dinâmico, esse snippet de código não faria com que o limite cook-eggs-enabledfosse liberado ao letterminar? Tenho certeza de que já encontrei um bug como esse antes. O defvar estava acontecendo dentro do let, e letdepois restaurou a variável ao seu estado inicial (nulo).
Malabarba
1
@ Malababa Não, essa é uma situação diferente. Veja o último parágrafo de Definindo variáveis para o motivo.
lunaryorn