I have a grown fairly large emacs configuration over the years and I am constantly tweaking it. I test out new packages and remove old ones that I haven’t been using. My original method for adding a new package to my emacs configuration was just to follow the install instructions verbatim from the README, but as I’ve been learning more about emacs and programming emacs lisp, I decided it was time to look into the different methods of including elisp code that were available.
I noticed that some packages use
load, some use
require, and others use
autoload and wondered what the differences were and which ones I should be using…
load-file are the most basic methods of including an elisp file into your configuration. Each one simply reads the file and executes the code. This means that if the
load expression is executed more than once the file contents will be executed more than once. There are times where this is useful, but I think that its safe to say that this is not something I desire for the normal case.
The difference between
load-file comes from the way the file is located on the system.
load will search through each directory in the list
load-path and if it finds the file there it will load it, while
load-file just looks directly at the path that you have specified.
require is a command that solves the problem of loading the same file more than once mentioned in the previous section. With
require, emacs will search for the appropriate file only if it hasn’t yet been loaded and then execute it. To use this the file must use the
provide function to define the feature that it includes. After this function has been executed (i.e. when the file has been loaded and the feature has been included), if
require is called again emacs will not try to re-execute the file.
require expects that the feature will be provided by a file of the same name located within the load path. This can be overridden using an optional second parameter to
require but I can’t really think of a reason I would want to do that very often.
autoload works a little differently than the other two functions, as it doesn’t load any file when it is called. Instead, its purpose is to delay the execution of the referenced file until the code within it is actually needed. You give
autoload the name of a function and a file that contains that function and, when the function is called — if the file hasn’t yet been loaded —
autoload will read and execute the lisp code from within the file. This is nice because it can greatly increase your startup time by not loading packages that you are not planning to use.
On the other side of the issue is each
autoload call only binds one function to a file. This means if you have 5 functions contained in a file that you want to trigger that file’s autoloading, you must call
autoload 5 separate times in your configuration.
Another potential problem with
autoload happens when you have some additional code you want to execute after the file has loaded (i.e. you want to change some of the keybindings defined within the file). If you were to set them at startup time, when one of the
autoload functions is called the file will be read/evaluated and your bindings will be overridden back to the defaults.
Luckily, emacs has already thought of this problem and provides us with
eval-after-load. This function allows us to define some additional code to evaluate after some specific code is loaded. In our previous scenario if we define our keybindings using
eval-after-load to execute the definitions after the
autoload, our code will be executed second and override the files keybindings back to the settings we want.
Applying this knowledge
After clearing up my understanding I have decided on a few things:
- I want to remove pretty much all
load-file calls from my configuration.
- I want to switch all of my modules that I can to use
- I want to use
autoload only rare occasions when loading the file takes a lot of time/resources.
My first instinct was to move as much as possible to
autoload but I realized that I don’t actually start emacs very frequently. I usually just leave it open and close and open buffers as necessary. For this reason I think I would find the sudden pause when loading a module that I am referencing for the first time more annoying than an increased startup time (YMMV).
Now that I’ve got a plan all that’s left is to actually do it.