ccc31807 <
cart...@gmail.com> writes:
> I've been writing some simple Common Lisp programs, and am now getting
> into more complicated ones. This is a very basic question about
> organizing a file structure.
>
> I use both clisp and sbcl on windows. To date, I've had good success
> with quicklisp, but asdf and mk:system both remain mysteries. I'm
> happy with quicklisp, but would like to find some simple documentation
> about alternatives. I may be very dense, but after many months of
> trying, I have not been able to use or understand asdf.
Forget mk:defsystem, it's an old system superseded by asdf.
If you have to deal with an old library using mk:defsystem, my advice
would be to write an asd file to replace it.
> I have downloaded and 'installed' (please note the scare quotes) a
> number of Common Lisp packages, but they remain mostly unusable for
> me. I have them scattered all over the place, and my Lisp session
> squawk badly if I try to use them.
You may install your lisp libraries manually installed in
~/quicklisp/localprojects/
quicklisp will automatically pick them up, if they have an asd file.
> First Question: How do I arrange my file structure with respect to my
> Common Lisp packages so that I don't have to jump through hoops just
> to use the functions they contain?
Files and CL packages are totally unrelated.
> Can I have them in one place, or do
> I have to have duplicate sets in /clisp and /sbcl?
Unless you're writing implementation specific code, (and even in this
case) there's no reason to separate much less to duplicate source code.
You don't duplicate source files for gcc, icc, cling, etc!?!
> Maybe I can but an instruction in my configuration files to tell the
> interpreters where the packages are located ... if so, I haven't found
> the documentation yet.
The package is not the unit of loading, so there's little point of
mapping CL packages with file locations.
So, to answer your first question: mu.
You don't arrange your file structure with respect to your CL packages.
You arrange your file structure according to some logical scheme useful
to your code editing habits.
One option would be to store all the methods specialized on a class in a
file named for that class. Then when you're thinking about debugging or
adding a method for that class, you can go directly to that file.
Another option would be to store all the methods implementing some
feature in a file named for that feature. Then when you're thinking
about debugging or modifying a feature, you can go directly to that
file.
Another option would be to store your functions and methods
alphabetically, in files named a.lisp, b.lisp, c.lisp, etc. So that
when you're thinking about debugging or modifying a function named xyz,
you can go directly to the file x.lisp.
Another option if you have a big program, with modules, is to have
subdirectories for each modules and files in each subdirectories.
Another option is to put everything in a single file. With emacs on
64-bit systems, you can edit files bigger than 2 GB, so you have some
margin here…
Now, the real good modern way to do it, would be to leave emacs store
your toplevel forms in the files it wants, and you wouldn't have to deal
with them. Depending on what you want to debug or modify or add, it
would present you various lists of toplevel forms: alphabetical, by
class, by feature, by category, by module, etc, that you could edit.
> Second Question: How do I import/use/load/require packages so that I
> can call/use the functions the packages contain?
Again, you're confusing different things. You cannot import, load or
require a CL package.
You can import a SYMBOL: (import 'some-package:some-symbol)
You can use a PACKAGE: (use-package :some-package)
You can load a FILE: (load "some-file.lisp")
You can require a MODULE: (require :some-module)
With asdf or quickload,
you can also load systems: (ql:quickload :some-system)
> Right now, I'm
> attempting to use cl-sqlite, cl-ppcre, cl-pdf, and cgi-utils.
Those are systems. You cannot use them, you can only quickload or
asdf:load-op them.
(ql:quickload :cl-ppcre)
> What would a program header look like to import the code so I can use the
> functions these packages contain, with or without a leading package
> qualifier?
There' no notion of program header in lisp. Only a notion of
compilation unit (which may be mapped to source files, but not
necessarily) and a notion of toplevel form.
> Question 3: Is there anything in Common Lisp (perhaps quicklisp) that
> works similar to CPAN? For those of you who don't know CPAN, it's a
> functionality that allows me to download, compile, test, and install
> Perl modules so that they can be used quickly and easily in programs.
Yes, there's quicklisp.
> Note 1: As I said, I have found quicklisp easy to use without actually
> understanding fully how it works, but I'd rather have the source of
> the packages on my machine so I don't have to rely on an internet
> connection to use them.
Yes, you found quicklisp easy to use without understand ANYTHING about
it. Bravo.
> Note 2: I'm at a point where I have a rather large amount of code in a
> number of packages, complete with asd files, so I might be asking a
> lot of tedious questions if I can't figure this out shortly.
There's a lot of documentation, tutorials, example programs and
libraries, and blog entries, describing all this stuff. Use google!
Since you sound dumb enough not knowing how to use google, I'll present
here a minimal project. Let's write an application that will produce
the factorization of the factorial of a number:
- the user enters a positive integer,
- the system computes the factorial of this number,
- the system computes the prime factors of the factorial,
- the prime factors are printed.
To compute the prime factors we'll use the FUNCTIONS bound to the
com.informatimago.common-lisp.arithmetic.primes:factorize and
com.informatimago.common-lisp.arithmetic.primes:print-factorization
SYMBOLS from the com.informatimago.common-lisp.arithmetic.primes PACKAGE
which are loaded by the com.informatimago.common-lisp.arithmetic SYSTEM.
We'll divide the program in two components: user-interface and compute.
We'll spread the toplevel forms we will write into those files:
fafa/packages.lisp
fafa/compute/factorial.lisp
fafa/user-interface/main.lisp
fafa/user-interface/validation.lisp
We will have our program be read in a single Common Lisp package, since
it's simple enough: we're alone to program it, and it will have only a
small number of definitions. But for more complex projects, written by
several programmers, and having a lot of definitions, we would define
several packages to match for example the component structure, so that
there's no risk of name collision for auxiliary definitions in the
various components.
----(fafa/packages.lisp)------------------------------------------------
(in-package :common-lisp-user)
(defpackage :com.informatimago.fafa
(:use :cl
:com.informatimago.common-lisp.arithmetic.primes)
(:export :main)
(:documentation "
Implements the fafa program: print factorial factorizations.
Copyright Pascal Bourguignon 2012 - 2012
License: AGPL3
"))
----(fafa/compute/factorial.lisp)---------------------------------------
(in-package :com.informatimago.fafa)
(defun factorial (n)
(loop
:for i :from 1 :upto n
:for prod = i :then (* prod i)
:finally (return prod)))
(defun factorial/test ()
(assert (= (factorial 10) 3628800))
(assert (= (factorial 20) 2432902008176640000))
:success)
(factorial/test)
----(fafa/user-interface/main.lisp)-------------------------------------
(in-package :com.informatimago.fafa)
(defun main ()
(format *query-io* "~%Fafa~%")
(let* ((n (loop
:for v = (let ((*read-eval* nil))
(format *query-io* "~%Please enter an integer between 1 and 18:")
(finish-output *query-io*)
(read *query-io*))
:until (validate v "an integer between 1 and 18" '(integer 1 18))
:finally (return v)))
(n! (factorial n))
(factorization (rest (factorize n!))))
(format t "~% ~A ! = ~A " n (first factorization))
(dolist (term (rest factorization))
(if (listp term)
(format t "* ~A^~A " (second term) (third term))
(format t "* ~A " term)))
(format t "~2%"))
(values))
----(fafa/user-interface/validation.lisp)-------------------------------
(in-package :com.informatimago.fafa)
(defun validate (object type-desc type)
(if (typep object type)
t
(progn
(format *error-output* "~%~S is not ~A, but a ~A~%"
object type-desc (type-of object))
nil)))
------------------------------------------------------------------------
Now, we want to load those FILES so that their definitions be available
to the program. Since all the source files contain an in-package
:com.informatimago.fafa form, the package definition form must be loaded
and evaluated first! So fafa/packages.lisp must be loaded first. Since
main.lisp uses functions defined in validation.lisp and in
factorial.lisp, they should probably be loaded first too. [Actually,
lisp allows compiling a function calling another function before that
later being defined, but most compilers would signal a warning, and for
other definitions (special variables macros, etc, this wouldn't be
allowed).]
So we have the rules that:
to load or compile user-interface/main.lisp, we must load first packages.lisp
user-interface/validation.lisp
and compute/factorial.lisp
to load or compile user-interface/validation.lisp, we must first load packages.lisp
to load or compile compute.lisp, we must first load packages.lisp
This translates into asdf as:
:components ((:file "user-interface/main" :depends-on ("packages"
"user-interface/validation"
"compute/factorial"))
(:file "user-interface/validation" :depends-on ("packages"))
(:file "compute/factorial" :depends-on ("packages"))
(:file "packages" :depends-on ()))
But before loading this system, we must load the system
com.informatimago.common-lisp.arithmetic, which translates in asdf as:
:depends-on ("com.informatimago.common-lisp.arithmetic")
So we can wrap that in an asdf:defsystem form.
Since there's a single CL package in that system, I will use the same
name for the system as the package, but systems can have and often have
different names. To let quicklisp and asdf find systems easily, we put
them in files named like the system they contain (but one could also put
different systems in the same file, I don't advise to do that however,
or to put anything else than an asdf:defsystem form in those asd files).
----(fafa/com.informatimago.fafa.asd)-----------------------------------
(asdf:defsystem :com.informatimago.fafa
:name "Fafa"
:description "Factorize Factorial"
:author "Pascal Bourguignon"
:version "1.0.0"
:license "AGPL3"
:depends-on ("com.informatimago.common-lisp.arithmetic")
:components ((:file "user-interface/main" :depends-on ("packages"
"user-interface/validation"
"compute/factorial"))
(:file "user-interface/validation" :depends-on ("packages"))
(:file "compute/factorial" :depends-on
("packages"))))
------------------------------------------------------------------------
Now if you've put fafa/ in ~/quicklisp/local-projects/fafa/ then you
just quickload it. If you've put it elsewhere, you can add it to
asdf:*central-registry*:
cl-user> (pushnew #P"~/fafa/" asdf:*central-registry* :test (function equalp))
(#P"/home/pjb/fafa/" #P"/home/pjb/quicklisp/quicklisp/")
Or, if you want to gather your own CL projects in a different directory
than ~/quicklisp/local-projects, then you can add the super directory to
ql:*local-project-directories*:
cl-user> (pushnew #P"~/src/" ql:*local-project-directories* :test (function equalp))
(#P"/home/pjb/src/" #P"/home/pjb/quicklisp/local-projects/")
assuming you put fafa/ in ~/src/fafa/.
Then you can quickload and run your program:
cl-user> (ql:quickload :com.informatimago.fafa)
To load "com.informatimago.fafa":
Load 1 ASDF system:
com.informatimago.fafa
; Loading "com.informatimago.fafa"
[package com.informatimago.fafa] ; <-- the package being defined
(:com.informatimago.fafa) ; <-- the systems been loaded
cl-user> (com.informatimago.fafa:main)
Fafa
Please enter an integer between 1 and 18:33
33 is not an integer between 1 and 18, but a (integer 0 1152921504606846975)
Please enter an integer between 1 and 18:5
5 ! = 5 * 3 * 2^3
; No value
cl-user>
--
__Pascal Bourguignon__
http://www.informatimago.com/
A bad day in () is better than a good day in {}.