4

I had a simple config to set fci-rule-column to different values depend on file types:

(use-package fill-column-indicator
  :ensure t
  :config
  (setq fci-rule-color "LightSlateBlue")
  (setq fci-handle-truncate-lines t)
  (add-hook 'python-mode-hook (lambda ()
                                (setq-default fci-rule-column 79)
                                (fci-mode t)))
  (add-hook 'c-mode-hook (lambda ()
                           (setq-default fci-rule-column 80)
                           (fci-mode t))))

You can see every time I add new file type, I have to repeat the work (add-hook ...). So I decide to refactor the code:

(use-package fill-column-indicator
  :ensure t
  :config
  (setq fci-rule-color "LightSlateBlue")
  (setq fci-handle-truncate-lines t)
  (defun my/fci-config (mode num)
    (add-hook mode (lambda ()
                     (progn
                       (setq-default fci-rule-column num)
                       (fci-mode t)))))
  (let (mode-config-hash)
    (setq mode-config-hash (make-hash-table :test 'equal))
    (puthash 'python-mode-hook 79 mode-config-hash)
    (puthash 'c-mode-hook 80 mode-config-hash)
    (puthash 'cperl-mode-hook 80 mode-config-hash)
    (maphash (lambda (k v) (my/fci-config k v)) mode-config-hash)))

With this approach, I only to put the mode and the number to hash table mode-config-hash when I configure setting for new file type.

But the code above did not work. I opened a Python or C file and the fci-mode was not enabled.

I try some debugging and realized that the line:

(setq-default fci-rule-column num)

did not ran correctly. If I change it to an integer value:

(setq-default fci-rule-column 79)

Then it worked.

So why did emacs see my variable? Is there a better way to accomplish this?

  • 2
    Start reading about "dynamic binding" vs "lexical binding". The former is the default. – phils Dec 22 '15 at 11:18
  • @phils: yeah, I read it before, but use let or even let-lexical with no luck. – cuonglm Dec 22 '15 at 11:19
  • 1
    A let binding is dynamic, unless lexical binding has been enabled for the library as a whole. If you used lexical-let with no success, show your code. Backquoting is a common solution in this sort of situation. – phils Dec 22 '15 at 11:28
  • @cuonglm Do you have lexical binding enabled in the file containing this code? – lunaryorn Dec 22 '15 at 14:48
  • @phils: Yeah, the lexical-let actually worked. I have some mis-typing in my attempt. It's now in my config github.com/Gnouc/emacs.d/commit/… . Thanks for your information. – cuonglm Dec 22 '15 at 15:19
8

Either enable lexical binding as a file-local variable in the file's first line, as described in the Elisp manual, node Using Lexical Binding, or just use this with dynamic binding (which is the default):

`(lambda ()
   (setq-default fci-rule-column ',num)
   (fci-mode t))

(The ' before ,num is not needed, except if you are unsure whether NUM will be a number and you want to go ahead and assign whatever is passed as NUM to variable fci-rule-column.)

This substitutes, at the time my/fci-config is invoked, the value of NUM that is passed to it, so there is no free variable in the resulting function (lambda form). The result of this code is a list. For example, if 42 is passed to my/fci-config as the value of its second arg then this is the resulting list:

(lambda ()
  (setq-default fci-rule-column '42)
  (fci-mode t))

The list will not be byte-compiled, because it is created (and evaluated) at runtime.

  • That's the downside -- not as efficient, by default. But you can byte-compile it explicitly using function byte-compile. And this is the only downside. Any other advantages of lexical binding (both lexical and dynamic binding have their advantages) do not apply here wrt variable NUM, since that variable has been eliminated.

  • The upside here is that the function that is the lambda form does not at all need any such variable, as a variable. That is, it does not use the variable as such; it needs only its (predefined) value. If the lambda form, or other code that it invokes, actually needed to do something with its free variable NUM, as a variable (e.g., set it, for other code), then it would make sense to keep it in the lambda body as a (free) variable. But in this case all you need is its value (which was passed to my/fci-config).

  • Yeah, and also using lexcal-let worked. See github.com/Gnouc/emacs.d/commit/… – cuonglm Dec 22 '15 at 15:25
  • Yes, lexical-let is a local alternative to turning on lexical-binding in a file. And a good one. – Drew Dec 22 '15 at 15:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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