I've been doing some network programming this weekend and I've hit anissue on which I'd like some suggestions.

I send a number of tyes of message between nodes ("Hi", "Here's ajob", "Success", etc), which means that each message sent needs tocarry a sentinel to identify what type it is, and, since debuggingissues from mismatched literals between the sending and receiving codeis vexing, I want to have a package with the shared constants that thecode for each side of the connection can use.

I started relatively simply (what passes for simple with me, anyway),a DEFPACKAGE form with the exports, and a macro that created theDEFCONSTANT forms.

This works, but has the ordinary issues related to the need to makeedits in two places.

It also has the extra problem that (since I'm working out protocolissues as I go) sometimes one constant needs to become two, and sbclfollows the spec and complains about package variance if the newexport list doesnt contain all of the symbols that were previouslyexported.

Obviously what I'd like is a single macro form that would let me makethe edit in one place, and take care of the need to UNEXPORT anysymbols not in the current list, I'm just having issues figuring outhow to do so "correctly".

The problem is that the symbols for the DEFCONSTANT forms for thePROGN must be interned in a package that won't exist until after thePROGN is processed.

This seems to work, even across loading from a FASL (on sbcl), but I'mnot sure that it should work. As I pointed out in another thread sometime ago, side-effects in macro-functions are a no-no, because theymay be run many times, in an interpreter, or none at all if theexpansion was saved in a FASL. (I suspect in this case the sbcl FASLformat has some way to refer to symbols from non-existent packages).

I'm wondering whether I should abandon this approach, require an emptyDEFPACKAGE that is left alone, and then a separate macro that does thebusiness with EXPORT, or perhaps another approach entirely.

Well, if you're using intern in the macro, that's already a side effect. So make-package is just one more side effect. It's like you said—if you need to make a defconstant form, then you can't do that without having a symbol at macro-expansion time. I came up with this macro for illustrative purposes:

So the macro I just wrote doesn't have side effects. But then the symbols won't be constants—they'll just be specials. The only alternative I can think of is to outsource the find-package/make-package dance to a reader macro—then when using the commands-package macro, you'd use the special syntax for the package form, which ensures that the package will exist when the macro gets expanded.

I'm sorry for not being more specific on the side-effects issue, it'snot that all side-effects should be banned in the host lisp, it's thatyou must ensure that any side-effects are executed in the target lisp.

I came across this issue myself when in my html generation library Iwanted to save some metadata about what attributes were valid for aparticular element so that it could be used at run-time.

I started by populating a hash-table stored in a special variable fromthe macro-function, this works the first time, but when I loaded froma fasl the cache was empty.

Incidentally, this took me ages to track down, because I didn'tsee it happen very often. I normally go weeks (or longer) betweenrestarting my lisp (generally when I reboot for kernel updates), andon top of that, I usually rebuild sbcl from git at the same time(which invalidates all fasls, and therefore meant that the next time Iloaded my web-apps it would be a full compile, rather than just aload, and the issue wouldn't present itself).

The correct way to do it was to change the macro expansion to includethe appropriate SETF form.

Back on the subject of my current problem:

I figured out what sbcl is doing so that it successfully loads fromfasls: it looks through the top-level PROGN, and turns thecompiled DEFPACKAGE form into a separate fasl-operation to theDEFCONSTANT forms, so by the time that it registers the constants thepackage does exist in the target lisp.

Unfortunately I don't think that I can reasonably assume this isportable

I appreciate the clarity, since my comment was intended to make the same point you're making about side effects. If you want something to "stick" in the FASL then you can't rely on side effects that don't stick across Lisp instances. Unlike you, I'm doing this on a laptop with Clozure CL, so I'm restarting it all the time. For that reason, obviously I'd notice if the stuff you're doing didn't work across FASL loads.

Of course, that's not what you're asking. You're asking if it's portable and works everywhere. Well, you have a package definition in a top-level progn before the constants are defined. By the Processing of Top Level Forms, those forms are also top-level and are processed sequentially. As you know, people tend to put the package definition in a separate file—which is recommended by the standard—but it's not technically required.