How to use Quickutil
- Utilities, Categories, and Symbols
- Loading Quickutil
- Loading Utilities
- Integrating Quickutil into Your Project
- Generating a Utility File
- Specifying the Package
- Autoloading Utilities
Utilities, Categories, and Symbols
Quickutil is centered around the idea of a utility, a self-contained piece of functionality or very related functionalities that typically solve a small, generic problem. Utilities are assigned a variety of categories, such as math
or sequences
. Categories can also act as micro-libraries, such as split-sequence
. Finally, utilities provide symbols, which might be functions, macros, constants, etc.
While it is most often the case, it is not necessarily true that a utility corresponds to a single symbol. For example, the utility :true-false
actually provides two symbols: true
and false
.
Quickutil allows one to query for functionality based on the three above granularities, all at the same time. It queries efficiently, too. So if you want the symbol ee
—the mathematical quantity e—specifically, as well as everything in the math
category, it will still only load the utility containing ee
once. Moreover, if you've already installed utilities, it is smart and neither recompiles nor reinstalls them.
Loading Quickutil
Currently, Quickutil is in beta, and not a part of Quicklisp. The latest Quickutil tarball can be downloaded into a place Quicklisp can see it, and it can be loaded via:
(ql:quickload :quickutil)
Loading this will create two packages: quickutil-client
or qtlc
for all of the acquisition and management functions, and quickutil
or qtl
for all of the installed utilities.
Loading Utilities
Loading utilities in Quickutil is done via the function qtlc:utilize
:
(qtlc:utilize
:utilities '(:iota :riffle)
:categories '(:alexandria)
:symbols '(:split-sequence-if-not))
This will acquire, compile, and load all of the requested utilities and dependencies right into the image to the quickutil
package.
Utilities are acquired locally, through an auxiliary, private package called quickutil-utilities
. As such, there is no network dependency whatsoever, and if Quickutil is up-to-date, then so will all of the utilities. This is unlike previous releases of Quickutil, which acquired all utilities off of the Internet.
For convenience, there are three functions with a subset of functionality for special cases:
(qtlc:utilize-utilities '(:riffle :weave))
- Install the utilities
:riffle
and:weave
. (qtlc:utilize-categories '(:math :alexandria))
- Install everything in the categories
math
andalexandria
. (qtlc:utilize-symbols '(:split-sequence-if-not))
- Install the utility containing the function
split-sequence-if-not
.
Integrating Quickutil into Your Project
There are two ways to integrate utilities from Quickutil into your project: adding a compile-time evaluated qtlc:utilize
form to your project or generating a stand-alone utility file. It is highly recommended that you use qtlc:utilize
in order to keep utilities up-to-date!
To integrate, simply add the appropriate qtlc:utilize
form somewhere in the toplevel of a file in your project and wrap it in eval-when
with :compile-toplevel
to ensure it gets evaluated at compile time. It is common to add it to one's utilities.lisp
file, which contains project-specific helper functions in addition to the utilize
form.
For example, from the Qsolve project:
(eval-when (:compile-toplevel)
(qtlc:utilize
:utilities '(:factorial :binomial-coefficient :mulf :divf
:copy-array :sort-copy
:while
:dohash :hash-table-key-exists-
Generating a Utility File
Sometimes a project may not want to depend on compile
, or a project needs to be bootstrapped without loading any ASDF systems. A lot of times, these projects have their own utils.lisp
file which inadvertently duplicates common utilities. Quickutil aims to manage that by generating it automatically based off of what you need.
Generating the utilities file is done with qtlc:save-utils-as
, which takes the utilities, categories, and symbols that you want to save.
(qtlc:save-utils-as "utils.lisp"
:utilities '(:iota :riffle)
:categories '(:alexandria)
:symbols '(:split-sequence-if-not)
:package "MY-PROJECT.UTILITIES")
Now, the file utils.lisp
will be saved to disk and can be added to your project. This file does not depend on Quickutil or have any network dependencies! It is a completely self-contained, readable, and modifiable source file. (However, we don't recommend modifying it so it can be regenerated in the future.)
To use it, simply add it as a dependency to your ASD file, or load it into the REPL. All of the utilities that you saved will be in the package quickutil
or qtl
.
Please note that it is
Specifying the Package
For projects that use utilize
, having everything go into the canonical quickutil
package is okay since it is managed by Quickutil. However, when one creates libraries or uses dependencies, it is necessary to create or use a dedicated package for generated utilities. If project X uses a generated Quickutil file, and also depends on project Y which also uses a generated file, then there is a clash between utility functions.
Quickutil requires the user to specify the package in which he or she would like to put the utilities, and optionally, specify it to be created if it hasn't been already. The function save-utils-as
takes a required argument :package
and two extra optional keyword arguments :ensure-package
and :package-nickname
:
:package
string- Specify that the utilities should be loaded into the package named by the string argument.
:ensure-package
boolean- Ensure that the package has been created. If the package does not already exist, Quickutil will create it. By default this is
t
. :package-nickname
string-or-nil- If a new package is created, specify that the nickname for the package should be the given string. If
nil
is specified, do not create a nickname. By default this isnil
.
So, for example, to save the following utilities and make them usable from the package named "MY-PROJECT.UTILITIES"
, we issue the following command:
(qtlc:save-utils-as "utils.lisp"
:utilities '(:iota :riffle)
:categories '(:alexandria)
:symbols '(:split-sequence-if-not)
:package "MY-PROJECT.UTILITIES"
:ensure-package t
:package-nickname "UTIL")
Now when this file is loaded, we will be able to use the functions from the specified package: my-project.utilities:iota
or util:iota
.
If we do not want to create a new package, because it was created elsewhere in your project, we can specify it not to. This is useful if you have other project-specific utility functions that you want to keep together with the generated ones.
(qtlc:save-utils-as "utils.lisp"
:utilities '(:iota :riffle)
:categories '(:alexandria)
:symbols '(:split-sequence-if-not)
:package "MY-PROJECT.UTILITIES"
:ensure-package nil)
Autoloading Utilities
Especially for quick REPL interactions, it is nice to be able to use utilities without explicitly loading them simply because it is faster and requires less typing. Quickutil provides an optional autoload syntax, #?
, which autoloads the symbol right after it. The syntax is disabled by default, and can be enabled with qtlc:enable-autoload-syntax
. For example:
> (qtlc:enable-autoload-syntax)
> (#?implode (#?shuffle (#?explode "hello")))
"lolhe"
Autoloading only acquires utilities when it needs to; already-loaded symbols are recycled.
You can permanently enable autoload syntax by loading Quickutil and putting the qtlc:enable-autoload-syntax
call in your Lisp's initialization file.