5

I get:

Debugger entered--Lisp error: (void-variable implicit-layout-active)

When using a variable declared as dynamically bound:

(void-variable implicit-layout-active)

I thought I cannot get void-variable issue for declared dynamically bound variables. Note that I do not use makunbound. But I do use lexical-binding: t for the module in question. What can this be?

More info here: https://github.com/haskell/haskell-mode/issues/854

7
  • 2
    Having looked at haskell-indentation.el, I'd say that the whole thing is flawed. The use of unprefixed dynamic variables is a major concern, and enabling lexical-scoping for a file that's supposed to work with Emacs 23 (according to some comments) is another, the more since there are places where the module explicitly works around the lack of lexical binding—which leads the whole purposes of lexical-binding: t ad absurdum.
    – user227
    Commented Sep 7, 2015 at 16:12
  • I'm voting to close this question as off-topic because it is based on the misconception that a dynamic (special) variable cannot be unbound, and the misconception that the user had given the variable a binding.
    – Drew
    Commented Sep 7, 2015 at 17:18
  • 3
    Well it's a misconception we don't share but that doesn't mean others don't.
    – tarsius
    Commented Sep 7, 2015 at 18:22
  • 1
    @Drew I respectfully disagree. It's a valid question for this site, in my opinion, even if it's based on a misconception.
    – user227
    Commented Sep 7, 2015 at 18:27
  • 1
    @lunaryorn: this module is undergoing renovation, hence the question. Emacs 24 support will be dropped, whole thing will not use any dynamic binding, but explicit state. Commented Sep 7, 2015 at 19:12

3 Answers 3

7

A special—a dynamically bound variable that is—variable can surely be void. From Void Variables (emphasis mine):

We say that a variable is void if its symbol has an unassigned value cell (see Symbol Components).

Under Emacs Lisp’s default dynamic scoping rule (see Variable Scoping), the value cell stores the variable’s current (local or global) value. Note that an unassigned value cell is not the same as having nil in the value cell. The symbol nil is a Lisp object and can be the value of a variable, just as any other object can be; but it is still a value. If a variable is void, trying to evaluate the variable signals a void-variable error, instead of returning a value.

In other words, the scoping rules of a variable are unrelated to whether it is actually bound or not. (defvar implicit-layout-active) declares the variable special, but it does not assign a value. The variable is still void. See for yourself:

ELISP> (defvar implicit-layout-active)
implicit-layout-active
ELISP> (special-variable-p 'implicit-layout-active)
t
ELISP> (boundp 'implicit-layout-active)
nil
ELISP> implicit-layout-active
*** Eval error ***  Symbol’s value as variable is void: implicit-layout-active

You need to assign a value, either in the declaration or later, to make a variable special and bound, i.e. non-void. Your code apparently does not do that.

Specifically, you'd need to use (defvar implicit-layout-active nil). But at this point you should definitely use prefixed names. Declaring unprefixed global variables is very bad style, and arguably somewhat rude towards other packages. And generally, haskell-indentation.el is not particularly friendly in this regard: It declares over a dozen of these. Please, please, please, don't do that. Sooner or later other Emacs package developers are going to hate you for this.

2
  • +1 I'm wondering whether I should delete my answer because we have three correct answers here :-) Commented Sep 7, 2015 at 16:38
  • 1
    No no, we have to keep the answers per question count up :-)
    – tarsius
    Commented Sep 7, 2015 at 18:16
4

You are not binding the variable dynamically. Edit: A sorry, I see now that you actually have declared the variable to be dynamically bound. But I am keeping the part of the answer which assumes otherwise anyway, as it might help others.

When lexical-binding is set to t, then let, and other forms, bind variables lexically by default - that's what that setting does.

However let can bind some variables lexically and others dynamically. What it should do for a particular variable, it figures out by checking whether the variable has been defined, or at least declared to be defined. So add this:

(defvar implicit-layout-active nil)

You most certainly also want to add a package prefix to that symbol and maybe add a doc-string explaining that the variable is only intended to be let-bound.

If you already have such a definition, but get the above error anyway, then that is probably because you use the variable before defining it. The order might also be wrong due to the use of outdated *.elc files.

Instead of the above you have used:

(defvar implicit-layout-active)

I.e. you are declaring that the variable is defined, without actually making sure that there always is a binding. When you now evaluate some code which tries to get the value of the variable without it actually being bound dynamically, well then it is void.

6
  • Variable is defined in global scope, it is used by calling functions from that module, that is after it has been loaded. (defvar implicit-layout-active nil) vs (defvar implicit-layout-active) does not make a difference, both fail with the same error described in questions. Commented Sep 7, 2015 at 12:39
  • Strange... Well let's see what others can come up with. ;-)
    – tarsius
    Commented Sep 7, 2015 at 14:46
  • @GracjanPolak You've made something wrong then. No offense meant, but there is a difference between these two forms: The former binds, the latter does not. In other words, with the former, boundp is non-nil, i.e. the variable is not void, whereas with the latter boundp is nil, meaning that the variable is in fact void.
    – user227
    Commented Sep 7, 2015 at 16:16
  • @tarsius +1, you're actually right: The OP does not bind the variable dynamically, they just declared it as a special, i.e. dynamically bound, variable.
    – user227
    Commented Sep 7, 2015 at 16:27
  • 1
    @tarsius: I retract my comment about (defvar x) being same as (defvar x nil). What confused me was that I got errors in both cases, but different errors. Commented Sep 7, 2015 at 19:11
1

I thought I cannot get void-variable issue for declared dynamically bound variables.

Your assumption is wrong. Dynamically bound (or “special” in other Lisps) variables can be unbound. In fact if you declare it like (taken from haskell-indentation.el):

(defvar implicit-layout-active) ;; is "off-side" rule active?

You are declaring variable implicit-layout-active without setting its value. We can read about it in doc-string:

defvar is a special form in ‘C source code’.

(defvar SYMBOL &optional INITVALUE DOCSTRING)

The optional argument INITVALUE is evaluated, and used to set SYMBOL, only if SYMBOL's value is void. If SYMBOL is buffer-local, its default value is what is set; buffer-local values are not affected. If INITVALUE is missing, SYMBOL's value is not set.

This answers your question: you've defined a dynamic variable and have not set its value. Obviously your code doesn't let bind it in some case and thus you get the error.


As for your comment that (defvar implicit-layout-active) is the same as (defvar implicit-layout-active nil) — you should check better. A few things to consider:

  1. When your test suite evaluates (require 'haskell-indentation) which module does it load: where you supply default value or “older” byte compiled version installed via package manager?

  2. Also note that defvar only sets value if variable is currently unbound (this is not actual issue in your case, just a word of caution).

Having said all this I can evaluate

(defvar implicit-layout-active nil)

and the dynamic variable is bound:

(boundp implicit-layout-active) ⇒ t

Now if I run your test case I get:

F haskell-indentation-check-23
    should not fail when seeing comments
    (ert-test-failed
     ((should
       (equal
        (cons
         (list ... ...)
         (haskell-indentation-find-indentations))
        current))
      :form
      (equal
       ((3 2))
       ((3 2)
        0))
      :value nil :explanation
      (proper-lists-of-different-length 1 2
                                        ((3 2))
                                        ((3 2)
                                         0)
                                        first-mismatch-at 1)))
1
  • Thanks for thorough help. I accepted the answer by lunaryorn because it is more general, but you should receive a badge for actually running the code in question :) Commented Sep 7, 2015 at 19:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.