emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

defcustom keyword :require


From: Drew Adams
Subject: defcustom keyword :require
Date: Fri, 31 Aug 2018 10:09:51 -0700 (PDT)

`defcustom' keyword `:require' has the effect of invoking `require'
for the defining library whenever `custom-set-variables' sets the
option value.  The `require' is invoked before setting the option.

This can be bothersome when a `custom-set-variables' sexp is inserted
automatically in an init file or `custom-file' by an Emacs session
where the option is defined, and the init file or `custom-file' is
subsequently used by an Emacs session where that library is not
available (e.g., cannot or should not be available).  A user might not
want Emacs to raise an error in such a case.

Example in Emacs 26.1: I save bookmarks, some of which are to remote
files.  This causes my `custom-set-variables' to be updated to include
the following entries, because (only for Emacs 26.1) the defcustoms
for `tramp-default-method' and `tramp-verbose' use `:require 'tramp':

 '(tramp-default-method "ftp" nil (tramp)) ; Require Tramp first
 '(tramp-verbose 9 nil (tramp))            ; Require Tramp first

Prior to and after Emacs 26.1 those entries are just the following, so
no attempt is made to load Tramp when processing the
`custom-set-variables':

 '(tramp-default-method "ftp")
 '(tramp-verbose 9)

Using an Emacs release that does not include Tramp aborts
initialization when it encounters the requirement to load Tramp.  So
in a session with an old Emacs release those require-Tramp settings
can raise this error:

Signaling: (file-error "Cannot open load file" "tramp")
  require(tramp)
  mapcar(require (tramp))
  custom-set-variables(...)

The option setting hard-requires Tramp. It would be OK here if the
meaning of the REQUEST part of the `custom-set-variables' settings
were to just soft-require the library, i.e., if the REQUEST arg meant
(require 'tramp nil t) instead of (require 'tramp).

OK, the Tramp example is problematic only for quite old Emacs releases
- but the point is general.

FWIW, this is how I now work around the problem in my init file, but
it is not a good solution (better solutions are welcome):

 (unless (require 'tramp nil t) (provide 'tramp))
 (load-file custom-file) ; Load only after faking providing Tramp

For Emacs 26.2 and later I guess either it was realized that Tramp
need not really be loaded prior to setting these options or some
change was made to the Tramp code to obviate the load.

The rationale given in the Elisp manual for :require is this:

  The most common reason to use `:require' is when a variable enables
  a feature such as a minor mode, and just setting the variable won't
  have any effect unless the code which implements the mode is
  loaded.

Seems like that common reason doesn't really call for a hard require
in general - a soft require might be sufficient.  The customized value
would not have the desired effect perhaps, but the world would not
end.

We already point out, for `define-minor-mode', that "Except in unusual
circumstances" the mode variable "must" be initialized to `nil':

  The initial value must be `nil' except in cases where (1) the mode
  is preloaded in Emacs, or (2) it is painless for loading to enable
  the mode even though the user did not request it.  For instance, if
  the mode has no effect unless something else is enabled, and will
  always be loaded by that time, enabling it by default is harmless.
  But these are unusual circumstances.  Normally, the initial value
  must be `nil'.

The Elisp manual also says this about :require (in node `Defining
Minor Modes'):

  One of the effects of making a minor mode global is that the MODE
  variable becomes a customization variable.  Toggling it through the
  Customize interface turns the mode on and off, and its value can be
  saved for future Emacs sessions (see (emacs)Saving Customizations).
  For the saved variable to work, you should ensure that the
  `define-minor-mode' form is evaluated each time Emacs starts; for
  packages that are not part of Emacs, the easiest way to do this is
  to specify a `:require' keyword.

This also suggests that (1) :require is for minor-mode MODE variables,
and (2) it is for packages that are not part of Emacs.  Those things
are not made clear in the part of the manual where :require is
introduced, which is maybe why those Tramp variables obtained :require
for Emacs 26.1.  The description of :require should probably make
clear that its intended use is particularly narrow.

Are there other, UNcommon reasons to use defcustom keyword :require?

If `custom-set-variables' raises an error for this in an init file it
stops everything.  In such a case (the common use case for :require)
wouldn't a warning be more appropriate?

Shouldn't :require really lead to a soft require and a warning, not a
hard require and an error?  Or should we perhaps add a :soft-require
keyword and promote its use (generally) over the use of :require?

Should the doc also point out this potential problem with (hard)
:require?  Should it tell `defcustom' writers that user customization
when using an Emacs session where the option exists will lead to
failure-to-launch in a session where the option's library is not
available?

FWIW, I have never run into this problem before - probably because (1)
few defcustoms actually use :require and (2) I always have the library
available, for any that do use :require.

Seems like defcustom :require doesn't really fit too well with
automatic writing of `custom-set-variables' to a user's init file or
`custom-file' (the most common use of `custom-set-variables'.  That
code is typically used for Emacs sessions of all kinds (e.g. different
releases, different contexts).

Maybe soft-requiring can handle its most common use cases?  Maybe
issuing a warning instead of erroring-out is generally more
appropriate?

And perhaps there could be a user option that lets you override
:require, to make it act always like :soft-require?  That is, even if
a library chooses (hard) :require, maybe users need a way to override
that.

What do you think?  I expect that few have actually run into this
problem, but I also expect that that is only because of the reasons
stated above: (1) :require is seldom used and (2) libraries that use
it are typically available across Emacs sessions.  (Most users
probably use only one Emacs release and use it with the same context
for each session.)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]