From: David Trudgett
Subject: Special variable woes?
Date: 
Message-ID: <m3fym2rdj0.fsf@rr.trudgett>
I think I'm having some problems with special variables in some code
of mine which I'm working on at the moment.

The problem is that my MAIL-MERGE function works just fine when I'm
IN-PACKAGE, but when I just USE-PACKAGE, it doesn't work. Here are the
bits that I think are relevant:


    (defpackage "TRUDGETT.DAVID.LATEX-LETTER"
      (:nicknames "LATEX-LETTER")
      (:use "COMMON-LISP"
            "TRUDGETT.DAVID.UTILS")
      (:export "MAIL-MERGE"))

    (in-package "LATEX-LETTER")



    ;;; PARAMETERS

    (defparameter *letter-params-file* "letter.param"
      "Name of file containing the parameter values for this letter.")

    (defparameter *sender-params-file* "sender.param"
      "Name of file containing sender information.")


    (defvar *letter-params* (make-hash-table)
      "Lookup table of letter parameters.")

    (defvar *sender-params* (make-hash-table)
      "Lookup table for sender information.")

    [...]
    
    
    (defun mail-merge (&key (letter-params-file *letter-params-file*)
                            (sender-params-file *sender-params-file*))
      (clear-params)
      (read-params letter-params-file *letter-params*)
      (read-params sender-params-file *sender-params*)
      (let* ((addresses (address-list (letter-param 'addresses-data-file)))
             (num-addresses (length addresses)))
        (with-open-file
         (out (letter-param 'output-file) :direction :output :if-exists :supersede)
         (write-string (prologue) out)
         (write-string (begin-document) out)
         (loop for address in addresses
               for count = 1 then (1+ count)
               until (= num-addresses count)
               do (progn #1=(write-string (letter address) out)
                         (write-string (new-page) out))
               finally #1#)
         (write-string (end-document) out))
        num-addresses))

    
Forget the ugly LOOP; that at least works! ;-) The problem seems to be
with those parameter defaults: *LETTER-PARAMS-FILE* and
*SENDER-PARAMS-FILE*. From out of package, they seem to show up as NIL
values (in the backtrace). Replacing those with literals, I still seem
to have trouble with the *LETTER-PARAMS* AND *SENDER-PARAMS* on lines
4 and 5, because (LETTER-PARAM 'ADDRESSES-DATA-FILE), a hash table
lookup, on the next line returns NIL NIL (the hash table was not
populated).

If anyone has an idea of what I'm doing wrong, and how I can fix it,
please let me know! :-)


David


-- 

David Trudgett
http://www.zeta.org.au/~wpower/

Though expressing the motives of their conduct differently, both those
in command and their subordinates are agreed in saying that they act
thus because the existing order is the order which must and ought to
exist at the present time, and that therefore to support it is the
sacred duty of every man.

    -- Leo Tolstoy, "The Kingdom of God is Within You"

From: Pascal Bourguignon
Subject: Re: Special variable woes?
Date: 
Message-ID: <873bi23frr.fsf@thalassa.informatimago.com>
David Trudgett <······@zeta.org.au.nospamplease> writes:
> [...]
>     (defun mail-merge (&key (letter-params-file *letter-params-file*)
>                             (sender-params-file *sender-params-file*))
>       (clear-params)
> [...]
> If anyone has an idea of what I'm doing wrong, and how I can fix it,
> please let me know! :-)

Well, it's hard to say because you didn't provide a functional
snippet, but the defaults of the keyword parameters don't seem to be a
problem:

[60]> (load"d.lisp")
;; Loading file d.lisp ...
;; Loaded file d.lisp
T
[61]> (TRUDGETT.DAVID.LATEX-LETTER::mail-merge)

*** - EVAL: undefined function TRUDGETT.DAVID.LATEX-LETTER::CLEAR-PARAMS


When I add: 

(defun clear-params ())
(defun read-params (&rest args))
(defun letter-param (&rest args) "/tmp/letter-param.out")
(defun address-list (&rest args))
(defun prologue () "<prologue/>")
(defun begin-document () "<document>")
(defun end-document () "</document>")
(defun new-page () "<page/>")
(defun letter (&rest args) "<letter></letter>")

There's no problem:

(load"d.lisp")
;; Loading file d.lisp ...
;; Loaded file d.lisp
T
[77]> (TRUDGETT.DAVID.LATEX-LETTER::mail-merge)
0
[78]> (cat "/tmp/letter-param.out")
<prologue/><document><letter></letter></document>

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"Klingon function calls do not have "parameters" -- they have
"arguments" and they ALWAYS WIN THEM."
From: David Trudgett
Subject: Re: Special variable woes?
Date: 
Message-ID: <m3accamozg.fsf@rr.trudgett>
Pascal Bourguignon <······@informatimago.com> writes:

> David Trudgett <······@zeta.org.au.nospamplease> writes:
>> [...]
>>     (defun mail-merge (&key (letter-params-file *letter-params-file*)
>>                             (sender-params-file *sender-params-file*))
>>       (clear-params)
>> [...]
>> If anyone has an idea of what I'm doing wrong, and how I can fix it,
>> please let me know! :-)
>
> Well, it's hard to say because you didn't provide a functional
> snippet, but the defaults of the keyword parameters don't seem to be a
> problem:

Yes, it's a bit hard to make a small snippet that works, and I didn't
want to overwhelm people with full code.

However, if you're of a mind to have a look, here is the full code at
the present point in time, together with a few supporting files to
make it all work.

    http://www.zeta.org.au/~wpower/dkt/programs/latex-letter/


And here is what I get from "outside" of the package:


Type-error in KERNEL::OBJECT-NOT-TYPE-ERROR-HANDLER:
   NIL is not of type (OR BASE-STRING STREAM PATHNAME)
   [Condition of type TYPE-ERROR]

Restarts:
  0: [ABORT-REQUEST] Abort handling SLIME request.
  1: [ABORT] Return to Top-Level.

Backtrace:
  0: (WILD-PATHNAME-P 1 NIL NIL)[:EXTERNAL]
  1: (OPEN NIL :DIRECTION :INPUT)
  2: (TRUDGETT.DAVID.LATEX-LETTER::ADDRESS-LIST NIL)
  3: (MAIL-MERGE :LETTER-PARAMS-FILE NIL :SENDER-PARAMS-FILE NIL)

>
> [60]> (load"d.lisp")
> ;; Loading file d.lisp ...
> ;; Loaded file d.lisp
> T
> [61]> (TRUDGETT.DAVID.LATEX-LETTER::mail-merge)
>
> *** - EVAL: undefined function TRUDGETT.DAVID.LATEX-LETTER::CLEAR-PARAMS
>
>
> When I add: 
>
> (defun clear-params ())
> (defun read-params (&rest args))
> (defun letter-param (&rest args) "/tmp/letter-param.out")
> (defun address-list (&rest args))
> (defun prologue () "<prologue/>")
> (defun begin-document () "<document>")
> (defun end-document () "</document>")
> (defun new-page () "<page/>")
> (defun letter (&rest args) "<letter></letter>")


That's above and beyond the "call of duty" as we say! I didn't want
anyone to go to that much trouble, as I was hoping I was doing
something breathtakingly stupid and obvious. Maybe it's just
breathtakingly stupid and not so obvious... :-)

Thanks for your help.

David



-- 

David Trudgett
http://www.zeta.org.au/~wpower/

"The high office of the President has been used to foment a plot to
destroy the American's freedom and before I leave office, I must
inform the Citizen of his plight." 

    -- John F. Kennedy, speaking at Columbia University, 
       10 days before his assassination
From: Pascal Bourguignon
Subject: Re: Special variable woes?
Date: 
Message-ID: <87psl61lan.fsf@thalassa.informatimago.com>
David Trudgett <······@zeta.org.au.nospamplease> writes:

> Pascal Bourguignon <······@informatimago.com> writes:
>
>> David Trudgett <······@zeta.org.au.nospamplease> writes:
>>> [...]
>>>     (defun mail-merge (&key (letter-params-file *letter-params-file*)
>>>                             (sender-params-file *sender-params-file*))
>>>       (clear-params)
>>> [...]
>>> If anyone has an idea of what I'm doing wrong, and how I can fix it,
>>> please let me know! :-)
>>
>> Well, it's hard to say because you didn't provide a functional
>> snippet, but the defaults of the keyword parameters don't seem to be a
>> problem:
>
> Yes, it's a bit hard to make a small snippet that works, and I didn't
> want to overwhelm people with full code.
>
> However, if you're of a mind to have a look, here is the full code at
> the present point in time, together with a few supporting files to
> make it all work.
>
>     http://www.zeta.org.au/~wpower/dkt/programs/latex-letter/
>
>
> And here is what I get from "outside" of the package:
>
>
> Type-error in KERNEL::OBJECT-NOT-TYPE-ERROR-HANDLER:
>    NIL is not of type (OR BASE-STRING STREAM PATHNAME)
>    [Condition of type TYPE-ERROR]

It's expecting a pathname somewhere.  
Perhaps your LETTER-PARAM function returns NIL?


(defun letter-param (param-name)
  "Return the value of PARAM-NAME in the letter parameters table."
  (print `(the result of (letter-param ',param-name) follows))
  (print (get-param param-name *letter-params*)))

[101]> (load"latex-letter.lisp")
;; Loading file latex-letter.lisp ...
;; Loaded file latex-letter.lisp
T
[102]> (TRUDGETT.DAVID.LATEX-LETTER::mail-merge)

(THE TRUDGETT.DAVID.LATEX-LETTER::RESULT TRUDGETT.DAVID.LATEX-LETTER::OF
 (TRUDGETT.DAVID.LATEX-LETTER::LETTER-PARAM
  'TRUDGETT.DAVID.LATEX-LETTER::ADDRESSES-DATA-FILE)
 TRUDGETT.DAVID.LATEX-LETTER::FOLLOWS) 
NIL 
*** - PARSE-NAMESTRING: argument NIL should be a pathname designator
       (OR STRING FILE-STREAM PATHNAME)

The following restarts are available:
ABORT          :R1      ABORT
Break 1 [103]> (maphash (lambda (k v) (print (list k v))) TRUDGETT.DAVID.LATEX-LETTER::*letter-params*)

(RIGHT-MARGIN "4.2cm") 
(LEFT-MARGIN "4.8cm") 
(BOTTOM-MARGIN "2cm") 
(TOP-MARGIN "3.5cm") 
(PAPER-SIZE "a4paper") 
(CLOSING-PHRASE "Yours faithfully") 
(PERSONAL-OPENING-SUFFIX ",") 
(PERSONAL-OPENING-PREFIX "Dear") 
(DEFAULT-OPENING "Dear Sir/Madam,") 
(ENCLOSURE "r\\'esum\\'e") 
(OUTPUT-FILE "letter.tex") 
(LETTER-TEXT-FILE "letter.txt") 
(ADDRESSES-DATA-FILE "addresses.data") 
NIL
Break 1 [103]> 


Indeed, it returns NIL.  And the reason is obvious. You've put in the
hash table symbols from the current package *package* instead of
putting there the actual keys you really use to access the parameters,
like: TRUDGETT.DAVID.LATEX-LETTER::ADDRESSES-DATA-FILE

Since you've created the hash tables with the default arguments, the
:test is (function eql), so you need to give the exact same symbol as
key.

The best would be to keep keywords: that's their whole reason de
vivre.  If you really want to use other symbols, at least intern them
in your package, using: 
   (intern (symbol-name item) :TRUDGETT.DAVID.LATEX-LETTER)
instead of:
   (intern (symbol-name item))
in:

(defun read-params (file table)
  "Read parameters from FILE and store them in hash TABLE."
  (with-open-file 
   (params file :direction :input)
   (let ((param-list (rest (read params nil nil))))
     (when param-list
       (let ((sym-name 'default)) 
	 (dolist (item param-list)
	   (if (keywordp item)
	       (setf sym-name (intern (symbol-name item)))
	     (setf (gethash sym-name table) item))))))))



-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

COMPONENT EQUIVALENCY NOTICE: The subatomic particles (electrons,
protons, etc.) comprising this product are exactly the same in every
measurable respect as those used in the products of other
manufacturers, and no claim to the contrary may legitimately be
expressed or implied.
From: David Trudgett
Subject: Re: Special variable woes?
Date: 
Message-ID: <m31wxmmm98.fsf@rr.trudgett>
Pascal Bourguignon <······@informatimago.com> writes:

> (ADDRESSES-DATA-FILE "addresses.data") 
> NIL
> Break 1 [103]> 
>
>
> Indeed, it returns NIL.  And the reason is obvious. You've put in the
> hash table symbols from the current package *package* instead of
> putting there the actual keys you really use to access the parameters,
> like: TRUDGETT.DAVID.LATEX-LETTER::ADDRESSES-DATA-FILE

<bashes-head-against-wall/> It was staring me in the face, after all!
Thanks for that! The subtleties of where a symbol lays its head to rest...


>
> Since you've created the hash tables with the default arguments, the
> :test is (function eql), so you need to give the exact same symbol as
> key.
>
> The best would be to keep keywords: that's their whole reason de
> vivre.

I'll try that way, yes.


Thanks again,

David


-- 

David Trudgett
http://www.zeta.org.au/~wpower/

You have never been invaded and never been under dictatorship.  So
like the frog in the boiling water you may not realize until it's too
late that you've lost your capacity to think for yourselves and accept
personal responsibility. That is the quiet danger facing America.

    -- Lech Walesa
       (Leader of Poland's Solidarity union &
       President of Poland 1990-1995) 
From: jayessay
Subject: Re: Special variable woes?
Date: 
Message-ID: <m364my3tmi.fsf@rigel.goldenthreadtech.com>
David Trudgett <······@zeta.org.au.nospamplease>  writes:

> I think I'm having some problems with special variables in some code
> of mine which I'm working on at the moment.
> 
> The problem is that my MAIL-MERGE function works just fine when I'm
> IN-PACKAGE, but when I just USE-PACKAGE, it doesn't work. Here are the
> bits that I think are relevant:

It's a bit unclear, but


>     (defpackage "TRUDGETT.DAVID.LATEX-LETTER"
>       (:nicknames "LATEX-LETTER")
>       (:use "COMMON-LISP"
>             "TRUDGETT.DAVID.UTILS")
>       (:export "MAIL-MERGE")) ;; <--- You don't export the specials here


>     (defun mail-merge (&key (letter-params-file *letter-params-file*)
>                             (sender-params-file *sender-params-file*))
>       (clear-params)
>       (read-params letter-params-file *letter-params*)
>       (read-params sender-params-file *sender-params*)
>       (let* ((addresses (address-list (letter-param 'addresses-data-file)))
>              (num-addresses (length addresses)))
>         (with-open-file
>          (out (letter-param 'output-file) :direction :output :if-exists :supersede)
>          (write-string (prologue) out)
>          (write-string (begin-document) out)
>          (loop for address in addresses
>                for count = 1 then (1+ count)
>                until (= num-addresses count)
>                do (progn #1=(write-string (letter address) out)
>                          (write-string (new-page) out))
>                finally #1#)
>          (write-string (end-document) out))
>         num-addresses))
> 
>     
> Forget the ugly LOOP; that at least works! ;-) The problem seems to be
> with those parameter defaults: *LETTER-PARAMS-FILE* and
> *SENDER-PARAMS-FILE*. From out of package, they seem to show up as NIL

Are you defining another mail-merge in a package which uses the above
package?  Or just trying to call mail-merge defined in the above
package from one which uses it?


/Jon

-- 
'j' - a n t h o n y at romeo/charley/november com