From: bufie
Subject: How to use with-open-file with variable arglist?
Date: 
Message-ID: <_Y6dnbMsoZs66ozVnZ2dnUVZ_jqdnZ2d@comcast.com>
I've got a situation where my code needs to run in two different 
environments, one of which supports arguments to with-open-file that are 
not supported on the other.

I'd like to have a simple check where I can use the same call, but with 
the additional arguments (part of the plist) in the second environment.

For example, the simpler environment might use a call similar to:

(with-open-file (o :direction :output)
   .... )

and the second would add something like:

(with-open-file (o :direction :output :encoding :iso8859-1)
   .... )

The first env won't work with the extra parameters.

What seems to be the ideal situation would be to build the plist and 
then just use that as the argument, but I haven't been able to make it 
work.  i.e. something like:

(let ((my-args (list :direction :output))
   (when env2  ;; test which env we're using
     (push :iso8859-1 myargs)
     (push :encoding myargs))
   (with-open-file (o myargs)
     .... )

Which doesn't work, of course, since myargs is a list which is not 
expected by with-open-file.  I thought I had seen a method for expanding 
the plist into its components, but I haven't been able to find it again.

Any thoughts or ideas would be greatly appreciated.

Thanks!

bufie

From: Pascal Bourguignon
Subject: Re: How to use with-open-file with variable arglist?
Date: 
Message-ID: <878wz2tp7j.fsf@hubble.informatimago.com>
bufie <·····@spamneggs.com> writes:

> I've got a situation where my code needs to run in two different
> environments, one of which supports arguments to with-open-file that
> are not supported on the other.
>
> I'd like to have a simple check where I can use the same call, but
> with the additional arguments (part of the plist) in the second
> environment.
>
> For example, the simpler environment might use a call similar to:
>
> (with-open-file (o :direction :output)
>   .... )
>
> and the second would add something like:
>
> (with-open-file (o :direction :output :encoding :iso8859-1)
>   .... )
>
> The first env won't work with the extra parameters.

(with-open-file (o :direction :output
                  #+sbcl  :encoding        #+scbl :iso8859-1
                  #+clisp :external-format #+clisp charset:iso-8859-1)
  ... )

> What seems to be the ideal situation would be to build the plist and
> then just use that as the argument, but I haven't been able to make it
> work.  i.e. something like:
>
> (let ((my-args (list :direction :output))
>   (when env2  ;; test which env we're using
>     (push :iso8859-1 myargs)
>     (push :encoding myargs))
>   (with-open-file (o myargs)
>     .... )
>
> Which doesn't work, of course, since myargs is a list which is not
> expected by with-open-file.  I thought I had seen a method for
> expanding the plist into its components, but I haven't been able to
> find it again.
>
> Any thoughts or ideas would be greatly appreciated.

Otherwise, you can re-implement a do-open-file function similar to the
macro, something like:

(defun do-open-file (thunk path &rest keys &key &allow-other-keys)
   (let ((stream (apply (function open) path keys)))
      ;; missing error processing!
      (unwind-protect (funcall thunk stream)
          (close stream))))

(apply (function do-open-file) (lambda (stream) ...) file 
     :direction :output other-args)

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

WARNING: This product attracts every other piece of matter in the
universe, including the products of other manufacturers, with a
force proportional to the product of the masses and inversely
proportional to the distance between them.
From: bufie
Subject: Re: How to use with-open-file with variable arglist?
Date: 
Message-ID: <LM-dnfCs44KMHYzVnZ2dnUVZ_oGjnZ2d@comcast.com>
Pascal Bourguignon wrote:
> (with-open-file (o :direction :output
>                   #+sbcl  :encoding        #+scbl :iso8859-1
>                   #+clisp :external-format #+clisp charset:iso-8859-1)
>   ... )

Thanks for the reply.  One more thing I forgot to mention -- the two 
environments are just different versions of the same lisp 
implementation, the earlier version of which didn't support encoding. 
As a result, I don't believe the above will work unless I'm able to 
somehow get the version information into the *features* variable, 
correct?  If so, is that something I can do?  (I've never used the above 
methods, so I'm learning!)

> Otherwise, you can re-implement a do-open-file function similar to the
> macro, something like:
> 
> (defun do-open-file (thunk path &rest keys &key &allow-other-keys)
>    (let ((stream (apply (function open) path keys)))
>       ;; missing error processing!
>       (unwind-protect (funcall thunk stream)
>           (close stream))))
> 
> (apply (function do-open-file) (lambda (stream) ...) file 
>      :direction :output other-args)

Hmmm.  Interesting.   I think I'd definitely like to stick with the 
tried-and-tested with-open-file if I can...

Thanks for your continued help!
From: Pascal J. Bourguignon
Subject: Re: How to use with-open-file with variable arglist?
Date: 
Message-ID: <7cwsmmjrj6.fsf@pbourguignon.anevia.com>
bufie <·····@spamneggs.com> writes:

> Pascal Bourguignon wrote:
>> (with-open-file (o :direction :output
>>                   #+sbcl  :encoding        #+scbl :iso8859-1
>>                   #+clisp :external-format #+clisp charset:iso-8859-1)
>>   ... )
>
> Thanks for the reply.  One more thing I forgot to mention -- the two
> environments are just different versions of the same lisp
> implementation, the earlier version of which didn't support
> encoding. As a result, I don't believe the above will work unless I'm
> able to somehow get the version information into the *features*
> variable, correct?  If so, is that something I can do?  (I've never
> used the above methods, so I'm learning!)

Yes.  For example, clisp puts a :UNICODE in *FEATURES* when it
supports unicode.

If the different versions of your implementation don't have a
differentiating keyword in *FEATURES*, you can put one yourself:

(when (support-that-feature-p (lisp-implementation-version))
   (pushnew my-package:that-feature-supported *features*))

(with-open-file (o :direction :output
                   #+my-package:that-feature-supported  :encoding
                   #+my-package:that-feature-supported  :iso8859-1)
   ...)

You can use keywords in *features*, but at the risk of some collision.
It's safer to put a symbol from your own package.


If you have long optional lists, you can use the dot notation:

(with-open-file (o :direction :output
                   . #+my-package:that-feature-supported  ( :encoding  :iso8859-1 :external-format :default )
                     #-my-package:that-feature-supported  nil)
   ...)


-- 
__Pascal Bourguignon__
From: bufie
Subject: Re: How to use with-open-file with variable arglist?
Date: 
Message-ID: <P9qdnVc9M85veYzVnZ2dnUVZ_ommnZ2d@comcast.com>
Pascal J. Bourguignon wrote:
> bufie <·····@spamneggs.com> writes:
> If the different versions of your implementation don't have a
> differentiating keyword in *FEATURES*, you can put one yourself:
> 
> (when (support-that-feature-p (lisp-implementation-version))
>    (pushnew my-package:that-feature-supported *features*))

Thanks for the reply -- this looks like a great idea.  So, (sorry to 
slow on this), it appears that something like this should work, but it 
doesn't.  It appears I need to have an external symbol for this, but 
have never done anything like this (or even used export), so clearly I'm 
doing something wrong.  What should I do differently?

(when (> my-lisp-version 8)
   (defvar has-enc (make-symbol "has-enc"))
   (export my-package::has-enc)
   (pushnew my-package:has-enc *features*))

It appears that, as you say, I can just use something like
(pushnew :has-ext-encoding *features*)

and get it to work -- is there anything wrong with doing that (as long 
as I give it a name that's not likely to be use anywhere else)?

Thanks for your help!
From: Pascal J. Bourguignon
Subject: Re: How to use with-open-file with variable arglist?
Date: 
Message-ID: <7chcdqj9fc.fsf@pbourguignon.anevia.com>
bufie <·····@spamneggs.com> writes:

> Pascal J. Bourguignon wrote:
>> bufie <·····@spamneggs.com> writes:
>> If the different versions of your implementation don't have a
>> differentiating keyword in *FEATURES*, you can put one yourself:
>> (when (support-that-feature-p (lisp-implementation-version))
>>    (pushnew my-package:that-feature-supported *features*))
>
> Thanks for the reply -- this looks like a great idea.  So, (sorry to
> slow on this), it appears that something like this should work, but it
> doesn't.  It appears I need to have an external symbol for this, but
> have never done anything like this (or even used export), so clearly
> I'm doing something wrong.  What should I do differently?

Well, either don't bother with packages, or learn about them and their uses.

Not bothering: use a keyword or an unqualified symbol (it will be
interned in the current package, normally COMMON-LISP-USER aka CL-USER).

Learn about packages:
(defpackage "MY-PACKAGE" (:use "CL") (:export "HAS-ENC"))

(when (> my-lisp-version 8)
   (pushnew my-package:has-enc *features*))

> It appears that, as you say, I can just use something like
> (pushnew :has-ext-encoding *features*)
>
> and get it to work -- is there anything wrong with doing that (as long
> as I give it a name that's not likely to be use anywhere else)?

Nothing 100% wrong.  Depends on the probabilities.  If you are expert
in probabilistic computing, no problem.  Next time a user calls you
with "Hey! I've got a bug here", try to answer: "That's no problem,
this bug appears randomly with a probability of only 0.0123".

-- 
__Pascal Bourguignon__
From: bufie
Subject: Re: How to use with-open-file with variable arglist?
Date: 
Message-ID: <4qGdnUO1ipz8ZIzVnZ2dnUVZ_hGdnZ2d@comcast.com>
Pascal J. Bourguignon wrote:

>> It appears that, as you say, I can just use something like
>> (pushnew :has-ext-encoding *features*)
>>
>> and get it to work -- is there anything wrong with doing that (as long
>> as I give it a name that's not likely to be use anywhere else)?
> 
> Nothing 100% wrong.  Depends on the probabilities.  If you are expert
> in probabilistic computing, no problem.  Next time a user calls you
> with "Hey! I've got a bug here", try to answer: "That's no problem,
> this bug appears randomly with a probability of only 0.0123".

Thanks again for the insight.  I decided to try this simple route using 
a highly-unlikely-to-be-used-for-anything-else keyword, and all appears 
to work!

Thanks for the great ideas!

bufie
From: John Thingstad
Subject: Re: How to use with-open-file with variable arglist?
Date: 
Message-ID: <op.t95yj7j5ut4oq5@pandora.alfanett.no>
P� Fri, 25 Apr 2008 09:57:17 +0200, skrev Pascal J. Bourguignon  
<···@informatimago.com>:

> bufie <·····@spamneggs.com> writes:
> If you have long optional lists, you can use the dot notation:
>
> (with-open-file (o :direction :output
>                    . #+my-package:that-feature-supported  ( :encoding   
> :iso8859-1 :external-format :default )
>                      #-my-package:that-feature-supported  nil)
>    ...)
>
>

That's a neat hack.. I hadn't thought of that one :)

--------------
John Thingstad
From: Richard M Kreuter
Subject: Re: How to use with-open-file with variable arglist?
Date: 
Message-ID: <87zlrikpv7.fsf@progn.net>
bufie <·····@spamneggs.com> writes:

> I've got a situation where my code needs to run in two different
> environments, one of which supports arguments to with-open-file that
> are not supported on the other.
>
> I'd like to have a simple check where I can use the same call, but
> with the additional arguments (part of the plist) in the second
> environment.
>
> For example, the simpler environment might use a call similar to:
>
> (with-open-file (o :direction :output)
>   .... )
>
> and the second would add something like:
>
> (with-open-file (o :direction :output :encoding :iso8859-1)
>   .... )
>
> The first env won't work with the extra parameters.
>
> What seems to be the ideal situation would be to build the plist and
> then just use that as the argument, but I haven't been able to make it
> work.  i.e. something like:
>
> (let ((my-args (list :direction :output))
>   (when env2  ;; test which env we're using
>     (push :iso8859-1 myargs)
>     (push :encoding myargs))
>   (with-open-file (o myargs)
>     .... )
>
> Which doesn't work, of course, since myargs is a list which is not
> expected by with-open-file.  I thought I had seen a method for
> expanding the plist into its components, but I haven't been able to
> find it again.

DESTRUCTURING-BIND?

(let ((plist (list :direction :output :external-format :utf-8)))
  (destructuring-bind (&key direction external-format #|other OPEN keys|#)
      plist
    (with-open-file (stream file :direction direction
                                 :external-format external-format)
      ...)))

--
RmK