From: Kaelin Colclasure
Subject: How to alias a setf generic function???
Date: 
Message-ID: <6HCc4.1751$oW3.23512@newsin1.ispchannel.com>
I have a requirement to run a core engine in two (or more) separate "host"
HTTP server environments. Each environment has its own version of a CLOS
class for an HTTP request. What I would like to do is to define a set of
"proxy accessors" that my code can use -- something along these lines:

#+host-a (progn
    (setf (symbol-function 'request-content-length)
      #'host-a:content-length)
    (setf (symbol-function 'request-headers)
      #'host-a:headers))

#+host-b (progn
    (setf (symbol-function 'request-content-length)
      #'host-b:http-request-content-length)
    (setf (symbol-function 'request-headers)
      #'host-b:http-request-header-list))

This is straightforward enough for the read accessors -- but how can I
similarly alias a CLOS setf generic function? I know I can get at the
function with e.g. #'(setf host-a:headers) -- but then what?

-- Kaelin

From: Pierre R. Mai
Subject: Re: How to alias a setf generic function???
Date: 
Message-ID: <87hfgskimp.fsf@orion.dent.isdn.cs.tu-berlin.de>
"Kaelin Colclasure" <······@everest.com> writes:

> #+host-b (progn
>     (setf (symbol-function 'request-content-length)
>       #'host-b:http-request-content-length)
>     (setf (symbol-function 'request-headers)
>       #'host-b:http-request-header-list))
> 
> This is straightforward enough for the read accessors -- but how can I
> similarly alias a CLOS setf generic function? I know I can get at the
> function with e.g. #'(setf host-a:headers) -- but then what?

Stilistically you should use fdefinition instead of symbol-function
anyway (IMHO), and with fdefinition the following should IMHO work
nicely:

(setf (fdefinition 'xxx) #'car)
(setf (fdefinition '(setf xxx)) #'(setf car))

(let ((mylist (list 'a 'b 'c)))
  (setf (xxx mylist) 'd)
  (xxx mylist))

=> D

Regs, Pierre.

-- 
Pierre Mai <····@acm.org>         PGP and GPG keys at your nearest Keyserver
  "One smaller motivation which, in part, stems from altruism is Microsoft-
   bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]
From: Tim Bradshaw
Subject: Re: How to alias a setf generic function???
Date: 
Message-ID: <ey37lho98k4.fsf@cley.com>
* Kaelin Colclasure wrote:
> I have a requirement to run a core engine in two (or more) separate "host"
> HTTP server environments. Each environment has its own version of a CLOS
> class for an HTTP request. What I would like to do is to define a set of
> "proxy accessors" that my code can use -- something along these lines:

> #+host-a (progn
>     (setf (symbol-function 'request-content-length)
>       #'host-a:content-length)
>     (setf (symbol-function 'request-headers)
>       #'host-a:headers))

> #+host-b (progn
>     (setf (symbol-function 'request-content-length)
>       #'host-b:http-request-content-length)
>     (setf (symbol-function 'request-headers)
>       #'host-b:http-request-header-list))

Wouldn't it be much more elegant to define your own abstraction layer?

(defun request-content-length (...)
  #+... ...
  #+... ...)

If you declare these things inline (or as macros (or define compiler
macros) if your implementation doesn't reliably inline) then they
should have zero overhead and be much less hacky.  And you can
document at the abstraction layer, and massage arguments &c as
appropriate.

> This is straightforward enough for the read accessors -- but how can I
> similarly alias a CLOS setf generic function? I know I can get at the
> function with e.g. #'(setf host-a:headers) -- but then what?

Same would go for this.  However if you really like the mashing
function cells, then using FDEFINITION might help.

--tim
From: Marco Antoniotti
Subject: I don't like #+/#-! (Re: How to alias a setf generic function???)
Date: 
Message-ID: <lwbt70okj4.fsf_-_@parades.rm.cnr.it>
Tim Bradshaw <···@cley.com> writes:

> * Kaelin Colclasure wrote:
> > I have a requirement to run a core engine in two (or more) separate "host"
> > HTTP server environments. Each environment has its own version of a CLOS
> > class for an HTTP request. What I would like to do is to define a set of
> > "proxy accessors" that my code can use -- something along these lines:
> 
> > #+host-a (progn
> >     (setf (symbol-function 'request-content-length)
> >       #'host-a:content-length)
> >     (setf (symbol-function 'request-headers)
> >       #'host-a:headers))
> 
> > #+host-b (progn
> >     (setf (symbol-function 'request-content-length)
> >       #'host-b:http-request-content-length)
> >     (setf (symbol-function 'request-headers)
> >       #'host-b:http-request-header-list))
> 
> Wouldn't it be much more elegant to define your own abstraction layer?
> 
> (defun request-content-length (...)
>   #+... ...
>   #+... ...)
> 
> If you declare these things inline (or as macros (or define compiler
> macros) if your implementation doesn't reliably inline) then they
> should have zero overhead and be much less hacky.  And you can
> document at the abstraction layer, and massage arguments &c as
> appropriate.
> 
> > This is straightforward enough for the read accessors -- but how can I
> > similarly alias a CLOS setf generic function? I know I can get at the
> > function with e.g. #'(setf host-a:headers) -- but then what?
> 
> Same would go for this.  However if you really like the mashing
> function cells, then using FDEFINITION might help.

This is an interesting post.  I have been kind of thinking along these
lines recently while writing some code that had to be conditionalized
on both the CL implementation and the underlying OS.
It turns out that I decided that #+/#- are not a very good way to do
this sort of things.  Instead, I would use CLOS (heavier, but cleaner)
to define a set of classes (some with single instances) to be used for
discrimination, pretty much in the way explained in Keene's book.

I.e. I would supersede chapter 25.1.3 of the CLHS.

A possible definition would be

(in-package "ENVIRONMENT") ; Name to be decided

(defclass lisp-implementation ()
  ((type :initarg :type :reader lisp-implementation-type)
   (version :initarg :version :reader lisp-implementation-version)))

(defclass software ()
  ((type :initarg :type :reader software-type)
   (version :initarg :version :reader software-version)))

(defclass operating-system (sofware) (#|...|#))

(defclass intercal-os (operating-system) (#|...|#))

(defclass zut-os (operating-system) (#|...|#))

(defclass machine () (#|...|#))

(defconstant +lisp-implementation+
   (make-instance 'lisp-implementation :type "ACME CL" :version "0.001"))

(defconstant +operating-system+
   (make-instance 'intercal-os :type "IntercalOS" :version "-4/3"))

;;; or...

(defconstant +operating-system+
   (make-instance 'zut-os :type "ZutOS" :version "1.34E19"))

You get the idea.  I am willing to do a better writeup of this idea if
I get some feedback.  Let me know if you are interested.

The main advantage would be to be able to have your CL system
dependent code to use CLOS to do the dispatching (and above all the
necessary re-definitions) instead of peppering your code with #+/#-.

E.g. suppose your code wants to provide a specialized
SAVE-WORKING-IMAGE which used the implementation dependent code.

Right now you would have to write something like

(defun save-working-image (filename)
  #+cmu (ext:save filename #'(lambda () ()))
  #+allegro ...)

This is not bad if you can keep all this dependencies in one place and
if this dependencies are not too many.

However, it may turn out that your system will have many of this
little conditionals dispersed over several files.  Not that this is
a bad thing per se, but I believe that the above example could be
really be written in a better way saying

(defgeneric save-the-image (cl os filename))

(defmethod save-the-image ((cl t) (os t) (filename pathname))
   (error "Cannot save image on filename ~S for CL ~S and OS ~S."
          filename cl os))

(defun save-working-image (filename)
  (save-the-image +lisp-implementation-type+
                  +operating-system-type+
                  filename))

Elsewhere, in a specialized file for CMUCL, you could have

;;; CMUCL runs on IntercalOS and on ZutOS! Didn't you know it?

(defmethod save-the-image ((cl cmucl) (os intercal-os) (f pathname))
  (ext:save f #'identity))

(defmethod save-the-image ((cl cmucl) (os zut-os) (f pathname))
  (ext:save (munge-filename-for-zut-os f) #'identity))


There are advantages as well.  All in all this would be a step toward
a more standardized way to handle implementation dependencies.

BTW. I am sure this is a Pattern in some Pattern book out there. :)
PS.  Try to do the same thing with C++ :)

Cheers

-- 
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - 06 68 10 03 17, fax. +39 - 06 68 80 79 26
http://www.parades.rm.cnr.it/~marcoxa
From: Tim Bradshaw
Subject: Re: I don't like #+/#-! (Re: How to alias a setf generic function???)
Date: 
Message-ID: <ey3yaa47kbo.fsf@cley.com>
* Marco Antoniotti wrote:


> This is an interesting post.  I have been kind of thinking along these
> lines recently while writing some code that had to be conditionalized
> on both the CL implementation and the underlying OS.
> It turns out that I decided that #+/#- are not a very good way to do
> this sort of things.  Instead, I would use CLOS (heavier, but cleaner)
> to define a set of classes (some with single instances) to be used for
> discrimination, pretty much in the way explained in Keene's book.

This is a very good idea (since I have been vaguely thinking along
these lines too I would say that...), but it doesn't entirely solve
the nastiest problem you get, which is that you need to avoid
implementations ever *reading* each other's code.  You can't do:

    (defmethod foo ((imp cmucl) ...)
      (cmucl-specific-package:frob ...))

    (defmethod foo ((imp allegro))
      (excl:frob ...))

because neither CMUCl nor Allegro can read this code.

So you either need #+ and #- or you need to put stuff off in different
files and define an abstraction layer.  It's definitely too easy to
end up using #+/#- when you should be defining an abstraction layer,
and lots of that makes code really horrible to read.

On the other hand, sometimes not using #+/#- leads to either massive
code duplication (with resulting extreme fragility if you ever want to
change anything) or seriously gratuitous abstraction.  In cases like:

	(defun frob (...)
	  ;; Obscuro-Lisp really needs the OBSCURE declaration here to
	  ;; compile code that is anything like reasonable.
          #+Obscuro-Lisp(declare (obscuro:obscure ...))
	  ... portable code ...)

So you either end up duplicating everything, or (the awful Lisp
solution) defining something like:

	(define-properly-declared-thingy frob (...) ...)

With associated

	(declare-code-properly-for-thingy (impl code))

stuff.

So now no-one reading your code can even work out what your function
definitions are, and neither can they easily tell that there may be
OBSCURE declarations for something.

--tim (who has a weakness for this latter approach)

	
From: Marco Antoniotti
Subject: Re: I don't like #+/#-! (Re: How to alias a setf generic function???)
Date: 
Message-ID: <lw1z7wczrd.fsf@parades.rm.cnr.it>
Tim Bradshaw <···@cley.com> writes:

> * Marco Antoniotti wrote:
> 
> 
> > This is an interesting post.  I have been kind of thinking along these
> > lines recently while writing some code that had to be conditionalized
> > on both the CL implementation and the underlying OS.
> > It turns out that I decided that #+/#- are not a very good way to do
> > this sort of things.  Instead, I would use CLOS (heavier, but cleaner)
> > to define a set of classes (some with single instances) to be used for
> > discrimination, pretty much in the way explained in Keene's book.
> 
> This is a very good idea (since I have been vaguely thinking along
> these lines too I would say that...), but it doesn't entirely solve
> the nastiest problem you get, which is that you need to avoid
> implementations ever *reading* each other's code.  You can't do:
> 
>     (defmethod foo ((imp cmucl) ...)
>       (cmucl-specific-package:frob ...))
> 
>     (defmethod foo ((imp allegro))
>       (excl:frob ...))
> 
> because neither CMUCl nor Allegro can read this code.
> 
> So you either need #+ and #- or you need to put stuff off in different
> files and define an abstraction layer.  It's definitely too easy to
> end up using #+/#- when you should be defining an abstraction layer,
> and lots of that makes code really horrible to read.

I agree with all you said.  As a matter of fact, it turns out that I
concentrate all the #+/#- in a single file which does the appropriate
loading/compiling of the implementation dependent files (it works out
nicely with MK:DEFSYSTEM :) )

(mk:defsystem "ZUT"
  :components (
               #+allegro (:file "allegro-dependencies")
               #+cmu     (:file "cmu-dependencies")
               ;; and so on
               ))

With DEFSYSTEM you could even think to add a switch to each component
specification and have something like

(mk:defsystem "ZUT"
  :components (
               (:file "allegro-dependencies" :if-featurep :allegro)
               (:file "cmu-dependencies" :if-featurep :cmu)
               ;; and so on
               ))

since you can always use strings and avoid interactions with the
reader.  In this case the DEFSYSTEM operations are performed on the
component iff the feature is present in *FEATURES*.

> On the other hand, sometimes not using #+/#- leads to either massive
> code duplication (with resulting extreme fragility if you ever want to
> change anything) or seriously gratuitous abstraction.  In cases like:
> 
> 	(defun frob (...)
> 	  ;; Obscuro-Lisp really needs the OBSCURE declaration here to
> 	  ;; compile code that is anything like reasonable.
>           #+Obscuro-Lisp(declare (obscuro:obscure ...))
> 	  ... portable code ...)
> 
> So you either end up duplicating everything, or (the awful Lisp
> solution) defining something like:
> 
> 	(define-properly-declared-thingy frob (...) ...)
> 
> With associated
> 
> 	(declare-code-properly-for-thingy (impl code))
> 
> stuff.
> 
> So now no-one reading your code can even work out what your function
> definitions are, and neither can they easily tell that there may be
> OBSCURE declarations for something.

Yep.  I am not claiming that what I proposed is either a panacea or a
complete substitute for #+/#-.  I just believe that it could make your
life easier in many situations.

Anyway, if anybody has any ideas about how to completely specify the
ENVIRONMENT package, please tell me.

Cheers

-- 
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - 06 68 10 03 17, fax. +39 - 06 68 80 79 26
http://www.parades.rm.cnr.it/~marcoxa
From: ArolAmbler
Subject: Re: How to alias a setf generic function???
Date: 
Message-ID: <20000117021419.28107.00000334@nso-fk.aol.com>
I am admittedly quite new to CLOS, but why is this just another situation of
involving the general "inheritance" WITH "delegation" pattern?  

The idea of the pattern is that you have, say currently 2 kinds of os (which
"conceptually" support the same functions, but with actual details of the open,
file read, close, etc. (or http, or what have you)  with a few "extra"
arguments, or whatnot).

Further, you anticipate the 2 may grow to n, (half a dozen or more).   

Secondly, there are several other "dimensions", where "os" is just one
"dimension", where there is currently "some" variation,
and the numbers of "points" along those dimensions are ALSO expected to grow.

Even with only a half-dozen "dimensions", and 2 or 3 "points" along each, the
number of "software configurations" is
completely unthinkable, if every combination needs to be coded for EACH case.

As examples of "other" dimensions that can easily affect the application:  "one
user/ many users"  "one cpu/n cpus",  
"small", "medium", "huge" real memory,  "transaction response time is
important/total overnight throughput is important".

The "general" "standard" solution I use is to, for each "dimension", create a
"base" "abstract" class, which sets the common "interface".   You can avoid the
multiplicative nighmare by keeping the things that depend on a demension in
that ONE
place.  If you are REALLY up against the wall, the few places where you MUST
specialize on more than one dimension are
"easy" to handle with clos defmethod, as it dispatches on all argument types.

As to the AWFUL reader conditionalization syntax: keep it clean.  Just put the
code for each "point" along a dimension in
a different file.  A simple file that contains "the files to load" allows the
"pick and choose".   If necessary, depending on 
circumstances, a recompile with heavy "inlining" (aka macros, if no better
choice) allows almost all "speed lost" to be
compiled out.  The loading, and compiling, can be done BEFORE customer ship,
and, lo and behold, you have a 
"product line" of something like 5 x 2 x 3 x 7 x 2 x 4 "differerent"
applications -- but very little code that is "different".


NOW: back to "setf" generic with CLOS.  Why is it really, any different from a
generic CLOS function that has one more
argument?    Although I have yet to write my first serious line of CLOS using
code, the "all" augument dispatch,  the "multiple"
inheritance, and the ability of deftype to define "and, or, not" types, SHOULD
allow the specification of a relatively few
defmethod signatures, that "cover" all possible combinations, by "delegation"
of the parts of the work that are "whatever",
to the "specific" subclass instances along the dimensions which exist in this
particular case.

Other than the "load-list", and the segregation of code to the "correct" file,
there is no problem, that I can see.  If you don't
like the file segregation, because "related" code is in separate files, then
simply "preprocess" the source code, using
any kind of simple "marker" symbols, to split the code into the various files
for each dimension. 


Or is there something I've missed, such as (deftype win-med (and os_class_win
memory-medium-sized)) being "illegal" in
CLOS, EVEN when os_class_win and memory-medium-sized having no common
ancesestors (other than the obilitory standand-class and t).  
But even if that is "illegal", it still "works", if we just make it a defclass
instead of a deftype...  And, unless there is a "abuse" of function naming, or
an unintentional naming collision, its hard to see why the order of
superclassess would matter.


Clearly the very large number of "most specific" potential subclasses, can
mostly remain anoymous:  Only the "n-slot" "configuration" object, with each
slot of the "abstract" base class for each dimension needs
to be made.   And it is by using DELEGATION, to its "members", 
able to actually be a subclass of all of the abstract "base" dimension
interface classes, and to "properly" handle the entire "variablity",
RELIABLY, without exhaustive testing of each and every combination of possible
product configurations.


Or, again,  WHY are "setf" generics different from "ordinary" generics?

(I've only read, so far, 2 books on CLOS, and only one time each - but
it didn't SEEM that setf was "crippled")...
From: Robert Monfera
Subject: Re: How to alias a setf generic function???
Date: 
Message-ID: <3883E3D5.A914F02A@fisec.com>
ArolAmbler wrote:

[idea on conditionalization elided]

I think the idea of multidimensional conditionalization with CLOS is
good, it is probably more of a question of resources, i.e., how many
customers you need to justify the maintenance of all those versions for
various options in such a systematic manner.  Also, if the code for a
performance-sensitive piece of code is conditionalized (e.g., you add a
declaration for a particular compiler, but not for the others), would it
not mean that you have to maintain two essentially identical versions?
Normally you would just put in another, conditionalized declaration.

Regarding SETF: on its own, it is not a function, it is a macro.

> (defclass dog ()
    ((color :accessor color)
     (breed :accessor breed)))
#<STANDARD-CLASS DOG>

> (defvar *tapi* (make-instance 'dog))

*TAPI*
> (setf (color *tapi*) 'light-brown)
LIGHT-BROWN
> (color *tapi*)
LIGHT-BROWN
> (macroexpand '(setf (color *tapi*) 'light-brown))
(LET* ((#:G286 *TAPI*) (#:G285 'LIGHT-BROWN))
  (FUNCALL #'(SETF COLOR) #:G285 #:G286))
T
> (functionp #'(setf color))
T

If you look at the macroexpansion, you see that the name of the setter
function is '(setf color), but this is one independent black box type of
function, not some composite.  What's composite is the name.

See CLHS entries about SETF, places and INCF.

> (macroexpand '(incf (breed *tapi*)))
(LET* ((#:G292 *TAPI*) (#:G291 (+ (BREED #:G292) 1)))
  (FUNCALL #'(SETF BREED) #:G291 #:G292))

(This was blind targeted, as I didn't understand what your problem with
SETF was.  If I missed it, maybe others won't or you can add some
details of the problem.)

Regards
Robert
From: ArolAmbler
Subject: Re: How to alias a setf generic function???
Date: 
Message-ID: <20000131104008.27562.00000566@nso-cm.aol.com>
"setf is a macro, not a function"

I am well aware of that.  But, if you read up on defsetfmethod, 
in EFFECT, although a list of the form (setf symbol) is legitimate to use as
the name of a function (perhaps, implementationally, there is a
"setf-function-slot" as well as a function slot in every symbol, or, perhaps,
the setf function is strored in a hash table value, keyed off the symbol.)   

Whatever the technique, the net result is that defsetf effectively must convert
the place to a function of one more argument than the place has.  The "extra"
argument is the value to be strored in the place.

As CLOS is not "separated" from standard "non-clos" Common Lisp, but well
integrated, it logically follows that setf functions are generic, as the
original post clearly seemed to understand.  NOTE: setf is a macro,  (and setq
is a special form), but BOTH will, under suitable circumstances, effectively
convert into function calls, where the NAME of the function is a LIST of two
elements: the first being setf, and the second being a symbol.  

All the "place" type macros, incf, rotatef, shiftf, etc. also have rather clear
semantics, even to include the POSSIBLE extension to "places" that can hold
"multiple" values (see 2nd ed of Common Lisp, the language, by Guy L. Steele,
Jr. about pages 123 throug 145.)
Yes, the "semantics" requires you to read that section at least three times,
before you START to implement it.  But, before the environment clarifications
(or changes, depending on your point of view), and before CLOS, I implemented
setf, defsetf, etc.   The difficulty is that constants, whether done with
defconstant, or quoting, CANNOT be setf.  It is explicitly allowed to coalese
them. 

In conjunction with setf, it is basically a BAD idea to ever initialize a
"variable" or "parameter" with a constant: the entire "value" should be
"fresh".   Otherwise, the things that were NOT eq at interpreter time, can be
eql at compile time, which leads to the "wrong" method being selected at
compile time.   Avoiding initial values for defvar or defpararameter avoids the
whole issue, as well as the problem of hurting a coalesed constant.