From: Philippe Lorin
Subject: How to xor?
Date: 
Message-ID: <42d4d90f$0$20082$636a15ce@news.free.fr>
I'm looking for a XOR to use like I use AND and OR. But the only things 
I found in the CLHS are:
   BIT-XOR, which works on bit arrays
   BOOLE-XOR, which, when used with BOOLE, works on integers
   LOG-XOR, which works on integers

CLisp seems to have XOR, but CMUCL and SBCL don't. Isn't there some 
standard function for this? Or am I wrong in wanting one?

Background: I need to check that two values are either both null or both 
non-null, and not a mix of null and non-null. I'd like to write:
(assert (not (xor (null v1) (null v2))))

From: Russell McManus
Subject: Re: How to xor?
Date: 
Message-ID: <8764vej4m3.fsf@cl-user.org>
Philippe Lorin <············@gmail.com> writes:

> I'm looking for a XOR to use like I use AND and OR. But the only
> things I found in the CLHS are:
>    BIT-XOR, which works on bit arrays
>    BOOLE-XOR, which, when used with BOOLE, works on integers
>    LOG-XOR, which works on integers
>
> CLisp seems to have XOR, but CMUCL and SBCL don't. Isn't there some
> standard function for this? Or am I wrong in wanting one?

I think this works:

(defmacro xor (v1 v2)
  `(not (eq (not ,v1) (not ,v2))))

-russ
From: Hannah Schroeter
Subject: Re: How to xor?
Date: 
Message-ID: <db3jdn$g5s$1@c3po.use.schlund.de>
Hello!

Russell McManus  <···············@yahoo.com> wrote:
>[...]

>I think this works:

>(defmacro xor (v1 v2)
>  `(not (eq (not ,v1) (not ,v2))))

Use macros only if functions don't work.

(defun xor (v1 v2)
  (not (eq (not v1) (not v2))))

>-russ

Kind regards,

Hannah.
From: Kent M Pitman
Subject: Re: How to xor?
Date: 
Message-ID: <u1x62dk0i.fsf@nhplace.com>
······@schlund.de (Hannah Schroeter) writes:

> Hello!
> 
> Russell McManus  <···············@yahoo.com> wrote:
> >[...]
> 
> >I think this works:
> 
> >(defmacro xor (v1 v2)
> >  `(not (eq (not ,v1) (not ,v2))))
> 
> Use macros only if functions don't work.
> 
> (defun xor (v1 v2)
>   (not (eq (not v1) (not v2))))

Incidentally, as a matter of history, after the publication of the
first CLTL in 1984, there was a meeting in 1986 in Monterrey CA to
talk about how to proceed.  Steel brought a list of things he thought
were obvious and non-controversial that he wanted to change.  Of
the people attending (many of whom were not original designers of
CLTL but new people who had read it and started to use the language
or make implementations and felt they should have a say in how it
moved forward), little concensus was to be had other than that a 
lot of people objected to a lot of the things on the list as non-obvious.

One of the items on that list was the addition of xor.  As I recall
what made it controversial was precisely this question of whether it
should be a macro or special form (for consistency with AND and OR) or
a function (because it somewhat accidentally is able to be, as an
accident of how it is computed).  Of the pre-defined operators by
CLTL, I believe PROG1 is the only one that could have been implemented
as a normal function, but is defined not to be...

Anyway, I just wanted to raise the issue that this is a well-known
issue of somewhat historical nature.

You can probably find out how the Monterrey meeting turned out in Google.
But in short, it's spectacular lack of ability to get consensus led
to a decision to start ANSI subcommittee X3J13...
From: Paul F. Dietz
Subject: Re: How to xor?
Date: 
Message-ID: <ka6dnSTTKvYqo0vfRVn-hg@dls.net>
Kent M Pitman wrote:

> One of the items on that list was the addition of xor.  As I recall
> what made it controversial was precisely this question of whether it
> should be a macro or special form (for consistency with AND and OR) or
> a function (because it somewhat accidentally is able to be, as an
> accident of how it is computed).  Of the pre-defined operators by
> CLTL, I believe PROG1 is the only one that could have been implemented
> as a normal function, but is defined not to be...
> 
> Anyway, I just wanted to raise the issue that this is a well-known
> issue of somewhat historical nature.

Now that CLOS is integrated into the language definition,
is there a similar generic controversy about whether functions
added to the language should be generic?

By the way, it seems to me that an XOR function should be binary,
and should return the true argument in the case where exactly
one of the two arguments is true.

	Paul
From: Kent M Pitman
Subject: Re: How to xor?
Date: 
Message-ID: <uvf3agisi.fsf@nhplace.com>
"Paul F. Dietz" <·····@dls.net> writes:

> Kent M Pitman wrote:
> 
> > One of the items on that list was the addition of xor.  As I recall
> > what made it controversial was precisely this question of whether it
> > should be a macro or special form (for consistency with AND and OR) or
> > a function (because it somewhat accidentally is able to be, as an
> > accident of how it is computed).  Of the pre-defined operators by
> > CLTL, I believe PROG1 is the only one that could have been implemented
> > as a normal function, but is defined not to be...
> > Anyway, I just wanted to raise the issue that this is a well-known
> > issue of somewhat historical nature.
> 
> Now that CLOS is integrated into the language definition,
> is there a similar generic controversy about whether functions
> added to the language should be generic?

Yes and no.

This issue pre-dated CLOS integration.  That is, it was there as long as 
any talk of CLOS.

The reason many operators are not generic is that deciding how to do
them generically is tricky and people disagree on the definition. I
recall the NIL project (JonL White, Jonathan Rees, and Rick Bryan) ran
afoul of a generic definition for LENGTH enough that let us worry. Consider:
 (defmethod length ((x cons)) (+ 1 (length (cdr x))))
 (defmethod length ((x null)) 0)
 (defmethod length ((x string)) (array-dimension x 0))
Now think about 
 (length '(a b . "foo"))
The point is that often independent definitions invite what I'll call the
"modularity problem" where definitions look ok in isolation but surprising
effects happen in combination.  Similar to what
 (+ 1 2 "foo")
might do if we allowed + to be generic.

In sum, there is "genericity" and there is "just plain overloading".  Lisp
has eschewed overloading.  But the main difference is some notion that there
is a dominant conceptual definition that defines identities on these things
such that they behave as something akin to well-formed groups in mathematics,
not merely as rogue mappings going every-which-way.  And if you opened
this issue, you'd get all kinds of that kind of thing.

We had a brief squabble over what to do with (append '(a b . c) 'd '(e . f))
in common lisp.  I believe NCONC and APPEND differ in their treatment of
these (don't have time to look this up, so could be misremembering) because
NCONC traditionally relied on some things that APPEND didn't, and we came 
up with a split theory.

There turns out to be huge debugging value in things being undefined, that
is, in a language being "sparse" in terms of what's defined in what's not.
Adding genericity not only gives power but it gives less ability to get
quick error detection at time of an error.  The more coercion that can happen,
the more a program will run along before hitting an error...  I've seen
programs run to completion with bad data coerced many ways along the way
to locally plausible choices.

So yes, it's like the other issue in terms of its both seeming obvious
and seeming differently obvious to others, and hence being controversial.

> By the way, it seems to me that an XOR function should be binary,
> and should return the true argument in the case where exactly
> one of the two arguments is true.

I disagree strongly.

The decision to use XOR as a binary truth operator is something you
can do personally without others doing it.  That is, the people
wanting to compatibly use a multi-arg situation should not be forced
to "find another name".  The generic name should go with the most
generic use; more specific names should go with more specific uses.

(That's just my opinion, but I might as well state it with the same
force that I feel it.)

The function you want is the one that would generalize to multiple
arguments differently than xor does, and the reason you want it in
binary is that only in the binary case do they have somewhat overlapping
meaning.  But you really want:
 (defun one-of (&rest args)
   (some #'identity args))
From: Tron3k
Subject: Re: How to xor?
Date: 
Message-ID: <1121574580.203744.89450@g43g2000cwa.googlegroups.com>
Kent M Pitman wrote:
> Now think about
>  (length '(a b . "foo"))

Intriguing possibility: Is it necessarily the wrong thing for that to
return 5? Maybe it should do just that, while a more specific function
LIST-LENGTH would throw an error.
From: Joe Marshall
Subject: Re: How to xor?
Date: 
Message-ID: <psthv7gs.fsf@comcast.net>
"Tron3k" <······@gmail.com> writes:

> Kent M Pitman wrote:
>> Now think about
>>  (length '(a b . "foo"))
>
> Intriguing possibility: Is it necessarily the wrong thing for that to
> return 5? Maybe it should do just that, while a more specific function
> LIST-LENGTH would throw an error.

Supposing you have a variable *FOO*.  You call 

  (length *foo*)
  =>  5

Should (elt *foo* 4) give you an error?  What should it return if
*foo* is '(a b . "foo")?  Could you SETF it?

What should (length (concatenate 'list *foo* *foo*)) return?

-- 
~jrm
From: Kent M Pitman
Subject: Re: How to xor?
Date: 
Message-ID: <ud5phpj26.fsf@nhplace.com>
Joe Marshall <·············@comcast.net> writes:

> "Tron3k" <······@gmail.com> writes:
> 
> > Kent M Pitman wrote:
> >> Now think about
> >>  (length '(a b . "foo"))
> >
> > Intriguing possibility: Is it necessarily the wrong thing for that to
> > return 5? Maybe it should do just that, while a more specific function
> > LIST-LENGTH would throw an error.
> 
> Supposing you have a variable *FOO*.  You call 
> 
>   (length *foo*)
>   =>  5
> 
> Should (elt *foo* 4) give you an error?  What should it return if
> *foo* is '(a b . "foo")?  Could you SETF it?
> 
> What should (length (concatenate 'list *foo* *foo*)) return?

One person's intrigue is another's catastrophe.  You seem to
implicitly suggest that no one is relying on getting an error message
here and that the change you propose is compatible.  Or else that the
compatibility concern is of no consequence.  See
 http://www.nhplace.com/kent/CL/x3j13-86-020.html
for historical context.

And what should (concatenate 'list *foo* *foo*) return, for that matter.
Just because you expect an output list doesn't mean you mean to treat
the inputs as lists... CL defines their type to be dynamically inspectable.

One reason ELT is interesting is that it's already inefficient for ELT to
worry someone will use it to write some algorithm that supposes random access.
At least now ELT can sniff at the type it's given and select an appropriate
algorithm for doing its work based on that type.  If the type can vary
recursively, you take away even that minimal class of dynamic optimization.

Remember that one reason we signal errors about non-homogeneity of type
is to permit programs that assume a local degree of homogeneity at least
in the container structure.  This, at least, is the motivation for ENDP.
ENDP makes very little remaining sense in a world where the type of the
object changes as you recurse, since it's all about the intention of it
not changing.

Questions like this affect the whole fabric of the language if you not only
make a random local change but start propagating its implications into 
changes around it.  That fact--of destabilization of time-tested sets of
operations--is why we didn't originally do the change to generic, and it's
why I stand by that now.

I don't have opposition to a new generic set of operations being written
as a library for those who like that kind of thing.  It's easy enough to do.
I don't say languages written with this as a basic premise will fail.
I just say that at some point if you morph the core of CL more than a certain
amount to suit a new community, you sacrifice the original community for
which it is built, most of whom are not clamoring for this change in order
to get their work done.  

Build the experiment first.  Find out if people use it exclusively and
abandon the old ways.  THEN argue that the old unused operators should
fall away and yield their names to the new.

Then again, since you can build a whole new TRON3K-COMMON-LISP package and
seed it with whatever you want without changing CL at all, I'm STILL not sure
why you would sacrifice the ability to cross-call old code without modification
and not just teach people to start compatibly in TRON3K-COMMON-LISP...
From: Kent M Pitman
Subject: Re: How to xor?
Date: 
Message-ID: <uhdetpjjn.fsf@nhplace.com>
"Tron3k" <······@gmail.com> writes:

> Kent M Pitman wrote:
> > Now think about
> >  (length '(a b . "foo"))
> 
> Intriguing possibility: Is it necessarily the wrong thing for that to
> return 5? Maybe it should do just that, while a more specific function
> LIST-LENGTH would throw an error.

Necessarily? No.

Likely? Yes.

While you can think up uses for it, there are also already tons of non-uses
(that is, errors wanting to be caught).  Making those things "work" is the
same as not detecting those errors.

It's so easy to write an operator that does what you suggest you might
in this case that one question is why you would want to perturb
others' existing code by an incompatible definition ...

But the main point is that whether this is right is a matter of "intention"
and any assertion that this is something that is dynamically knowable is
improper.  As such, the decision is intrinsically arbitrary, and favoring
compabitility in the arbitrary seems a good plan to me...
See http://www.nhplace.com/kent/PS/EQUAL.html for more exposition on this.
From: M Jared Finder
Subject: Re: How to xor?
Date: 
Message-ID: <npqdnaT2F__U40ffRVn-sA@speakeasy.net>
Kent M Pitman wrote:
> "Tron3k" <······@gmail.com> writes:
>>Kent M Pitman wrote:
>>
>>>Now think about
>>> (length '(a b . "foo"))
>>
>>Intriguing possibility: Is it necessarily the wrong thing for that to
>>return 5? Maybe it should do just that, while a more specific function
>>LIST-LENGTH would throw an error.
> 
> Necessarily? No.
> 
> Likely? Yes.
> 
> While you can think up uses for it, there are also already tons of non-uses
> (that is, errors wanting to be caught).  Making those things "work" is the
> same as not detecting those errors.

I agree with this, but I do not see how that makes genericizing the 
sequence functions any more difficult.  Require LENGTH to be passed a 
sequence, and not an improper list.  If passed an improper list, length 
should raise a condition of type type-error, just like it does now.

This disallows the "clever" implementation of length for lists that you 
listed:

>  (defmethod length ((x cons)) (+ 1 (length (cdr x))))
>  (defmethod length ((x null)) 0)

So don't implement it that way!

   -- MJF
From: Paul F. Dietz
Subject: Re: How to xor?
Date: 
Message-ID: <FYWdnZhfFqjE4kffRVn-ig@dls.net>
M Jared Finder wrote:

> This disallows the "clever" implementation of length for lists that you 
> listed:
> 
>>  (defmethod length ((x cons)) (+ 1 (length (cdr x))))
>>  (defmethod length ((x null)) 0)
> 
> 
> So don't implement it that way!

The reason I would like to see functions like LENGTH implemented
as generic functions is not so that this clever (but wrong,
as Kent points out) implementation could be used, but so that
users could add their own sequence types (and, elsewhere, their
own streams, and hash tables, and so on).

	Paul
From: Kent M Pitman
Subject: Re: How to xor?
Date: 
Message-ID: <uirz8kgpu.fsf@nhplace.com>
"Paul F. Dietz" <·····@dls.net> writes:

> M Jared Finder wrote:
> 
> > This disallows the "clever" implementation of length for lists that
> > you listed:
> >
> >>  (defmethod length ((x cons)) (+ 1 (length (cdr x))))
> >>  (defmethod length ((x null)) 0)
> > So don't implement it that way!
> 
> The reason I would like to see functions like LENGTH implemented
> as generic functions is not so that this clever (but wrong,
> as Kent points out) implementation could be used, but so that
> users could add their own sequence types (and, elsewhere, their
> own streams, and hash tables, and so on).

I don't dispute this.  However, I personally think the right way to do
this (in CL--different languages have different sensibilities) is to
provide a protocol hookup to the sequence class.

The problem with allowing different sequence classes without requiring
them to be subclasses of sequence is accidentally allowing other things.  

It's bad enough that NIL is sometimes a non-list and sometimes a list.
e.g., consider (string 'nil) vs (coerce nil 'string).  It's just plain
hard to get this right.  Whereas defining your own operators as you like
is easier.
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: How to xor?
Date: 
Message-ID: <87pstd4lge.fsf@qrnik.zagroda>
Kent M Pitman <······@nhplace.com> writes:

> The reason many operators are not generic is that deciding how to do
> them generically is tricky and people disagree on the definition. I
> recall the NIL project (JonL White, Jonathan Rees, and Rick Bryan)
> ran afoul of a generic definition for LENGTH enough that let us
> worry. Consider:
>  (defmethod length ((x cons)) (+ 1 (length (cdr x))))
>  (defmethod length ((x null)) 0)
>  (defmethod length ((x string)) (array-dimension x 0))
> Now think about 
>  (length '(a b . "foo"))

The fault is not in length being generic, but in the definition of
length for cons or in the existence of improper lists (it's enough
to fix either of them).

> The point is that often independent definitions invite what I'll call the
> "modularity problem" where definitions look ok in isolation but surprising
> effects happen in combination.  Similar to what
>  (+ 1 2 "foo")
> might do if we allowed + to be generic.

It should be an error: + should work only for numbers, string concatenation
is a sufficiently different operation. But it doesn't mean that + shouldn't
be generic.

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/
From: Sam Steingold
Subject: Re: How to xor?
Date: 
Message-ID: <u4qapttul.fsf@gnu.org>
> * Marcin 'Qrczak' Kowalczyk <······@xaz.bet.cy> [2005-07-20 16:10:41 +0200]:
>
> Kent M Pitman <······@nhplace.com> writes:
>
>> The point is that often independent definitions invite what I'll call the
>> "modularity problem" where definitions look ok in isolation but surprising
>> effects happen in combination.  Similar to what
>>  (+ 1 2 "foo")
>> might do if we allowed + to be generic.
>
> It should be an error: + should work only for numbers, string
> concatenation is a sufficiently different operation. But it doesn't
> mean that + shouldn't be generic.

this is confusing.
on one hand, you say that "+ should work only for numbers".
   since there is no way to create a new kind of numbers,
   the implementation can implement + as a non-generic function
   without losing any functionality.
on the other hand, you say "it doesn't mean that + shouldn't be generic"
   what's the point?  you cannot add a method anyway!
   you cannot define a new kind of number!

Yeah, you might find it tempting to be able to write
(defclass quaternion (number) ...)
(defmethod + ((x quaternion) (y number)) ...)

But as soon as you do, you need to define also * methods.
note that quaternion multiplication is not commutative, and octonions
are not even associative, so the usual properties of * that many
compilers use will now be broken (unless the compiler can establish
argument types).

yes, the "demo" or "toy" value of CL would increase
if all the functions were generic,
but CL is not a "demo" language.
CL is the tool for solving _hard_ problems.
Why would you want to "overload" + in a large project
(where it will confuse people)?

-- 
Sam Steingold (http://www.podval.org/~sds) running w2k
<http://www.honestreporting.com> <http://www.jihadwatch.org/>
<http://ffii.org/> <http://www.memri.org/> <http://www.dhimmi.com/>
Politically Correct Chess: Translucent VS. Transparent.
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: How to xor?
Date: 
Message-ID: <87mzohh60h.fsf@qrnik.zagroda>
Sam Steingold <···@gnu.org> writes:

>> It should be an error: + should work only for numbers, string
>> concatenation is a sufficiently different operation. But it doesn't
>> mean that + shouldn't be generic.
>
> this is confusing.
> on one hand, you say that "+ should work only for numbers".
>    since there is no way to create a new kind of numbers,

There should be.

> note that quaternion multiplication is not commutative,

I don't mean quaternions, but e.g. decimal fixed point numbers for
currencies (more efficient than rationals), or lazily computed
arbitrary precision approximations of real numbers.

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/
From: Sam Steingold
Subject: Re: How to xor?
Date: 
Message-ID: <uu0ipsc5j.fsf@gnu.org>
> * Marcin 'Qrczak' Kowalczyk <······@xaz.bet.cy> [2005-07-20 17:05:50 +0200]:
>
> I don't mean quaternions, but e.g. decimal fixed point numbers for
> currencies (more efficient than rationals),

neither rationals nor integers (another common advice) are good for
currencies.
numbers can be multiplied by each other.
currencies cannot.
what is (* $10 $15)?
$$150?!

-- 
Sam Steingold (http://www.podval.org/~sds) running w2k
<http://www.camera.org> <http://www.openvotingconsortium.org/>
<http://www.dhimmi.com/> <http://www.honestreporting.com>
MS Windows: error: the operation completed successfully.
From: Juliusz Chroboczek
Subject: Re: How to xor?
Date: 
Message-ID: <7isly9cu7q.fsf@lanthane.pps.jussieu.fr>
Ingvar <······@hexapodia.net>:

> I'd say that a currency multiplied with another currency is a domain
> fault.

So how do you compute the variation of the amount of computing power
at a fixed budget (measured in SPECint per square Euro)?

                                        Juliusz
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: How to xor?
Date: 
Message-ID: <87mzoh2ka0.fsf@qrnik.zagroda>
Sam Steingold <···@gnu.org> writes:

> neither rationals nor integers (another common advice) are good for
> currencies.
> numbers can be multiplied by each other.
> currencies cannot.
> what is (* $10 $15)?
> $$150?!

I meant dimensionless decimal fractions, commonly called "decimal" or
"currency" in other languages. See http://www.python.org/peps/pep-0327.html
for example.

Adding units is another story that I never looked at.

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/
From: Pascal Bourguignon
Subject: Re: How to xor?
Date: 
Message-ID: <87irz59wmo.fsf@thalassa.informatimago.com>
Marcin 'Qrczak' Kowalczyk <······@knm.org.pl> writes:

> Sam Steingold <···@gnu.org> writes:
>
>>> It should be an error: + should work only for numbers, string
>>> concatenation is a sufficiently different operation. But it doesn't
>>> mean that + shouldn't be generic.
>>
>> this is confusing.
>> on one hand, you say that "+ should work only for numbers".
>>    since there is no way to create a new kind of numbers,
>
> There should be.
>
>> note that quaternion multiplication is not commutative,
>
> I don't mean quaternions, but e.g. decimal fixed point numbers for
> currencies (more efficient than rationals), or lazily computed
> arbitrary precision approximations of real numbers.


GPL'ed code follows. See invoice.lisp from:
cvs -z3 -d ··················@cvs.informatimago.com:/usr/local/cvs/public/chrooted-cvs/cvs co common/common-lisp


;;;---------------------------------------------------------------------
;;; Monetary Amounts & Currency Syntax
;;;---------------------------------------------------------------------

;; Since floating point arithmetic is not adapted to accounting,
;; we will use integers for monetary amounts, and
;; percentages will be expressed as rationnals: 16 % = 16/100
;;
;; 123.45 � = 12345 � = #978m123.45
;;
;; In addition monetary amounts are tagged with a currency, and
;; arithmetic operations are type-restricted.
;;
;; The reader syntax is: # [currency-code] m|M [+|-] digit+ [ . digit* ]
;; The currency code must be a numeric code of a currency found 
;; in (com.informatimago.common-lisp.iso4217:get-currencies), 
;; otherwise a read-time error is issued.
;; When the currency-code is not present, the currency designated 
;; by *DEFAULT-CURRENCY* is used.
;; The number of digits after the decimal point must not be superior 
;; to the minor unit attribute of the currency.
;; The value is converted to an integer number of minor unit and
;; the read result is an AMOUNT structure gathering the currency 
;; and the value.
;;
;; The operations defined on AMOUNT values are:
;; 
;; c: amount* --> boolean
;; with c in { <, <=, >, >=, =, /= }.
;;
;; +: amount* --> amount
;; -: amount* --> amount
;; *: amount X real* --> amount (commutatif and associatif)
;; /: amount X real* --> amount (not commutatif and not associatif)
;;
;; [ set* = Kleene closure of the set ]
;;
;; For now, all these operations work only when the currency of all amount
;; involved is the same.
;;
;; These Common-Lisp operators are shadowed, and functions are defined for 
;; them, that extend the normal numeric functions for amounts.
;;
;; The AMOUNT structure has a printer that prints different format
;; depending on the *PRINT-READABLY*. It uses the reader syntax defined 
;; above when *PRINT-READABLY* is true, or a "~V$ ~3A" format printing 
;; the value followed by the alphabetic code of the currency.


(DEFSTRUCT (AMOUNT (:PREDICATE AMOUNTP) (:PRINT-OBJECT PRINT-OBJECT))
  "An amount of money."
  CURRENCY
  (VALUE 0 :TYPE INTEGER))


(DEFMETHOD PRINT-OBJECT ((SELF AMOUNT) STREAM)
  (IF *PRINT-READABLY*
      (FORMAT STREAM "#~DM~V$"
              (CURRENCY-NUMERIC-CODE (AMOUNT-CURRENCY SELF))
              (CURRENCY-MINOR-UNIT (AMOUNT-CURRENCY SELF))
              (AMOUNT-MAGNITUDE SELF))
      (FORMAT STREAM "~V$ ~A"
              (CURRENCY-MINOR-UNIT (AMOUNT-CURRENCY SELF))
              (AMOUNT-MAGNITUDE SELF)
              (CURRENCY-ALPHABETIC-CODE (AMOUNT-CURRENCY SELF))))
  SELF);;PRINT-OBJECT


(DEFMETHOD CURRENCY ((SELF number))
  nil)


(DEFMETHOD CURRENCY ((SELF AMOUNT))
  (AMOUNT-CURRENCY SELF))


(defmethod amount-magnitude ((self number))
  self)


(defmethod AMOUNT-MAGNITUDE ((SELF amount))
  "
RETURN: A real equal to the value of the amount.
"
  (* (AMOUNT-VALUE SELF)
     (AREF #(1 1/10 1/100 1/1000 1/10000)
           (CURRENCY-MINOR-UNIT (AMOUNT-CURRENCY SELF)))))


(DEFPARAMETER *ZERO-AMOUNTS* (MAKE-HASH-TABLE :TEST (FUNCTION EQ))
  "A cache of 0 amount for the various currencies used.")


(DEFUN AMOUNT-ZERO (CURRENCY)
"
RETURN: A null amount of the given currency.
"
  (LET ((ZERO (GETHASH (FIND-CURRENCY CURRENCY) *ZERO-AMOUNTS*)))
    (UNLESS ZERO
      (SETF ZERO
            (SETF (GETHASH (FIND-CURRENCY CURRENCY) *ZERO-AMOUNTS*) 
                  (MAKE-AMOUNT :CURRENCY (FIND-CURRENCY CURRENCY) :VALUE 0))))
    ZERO));;AMOUNT-ZERO


(DEFMETHOD ABS       ((SELF NUMBER)) (COMMON-LISP:ABS   SELF))
(DEFMETHOD ABS       ((SELF AMOUNT)) 
  (MAKE-AMOUNT :CURRENCY (AMOUNT-CURRENCY SELF)
               :VALUE    (COMMON-LISP:ABS   (AMOUNT-VALUE SELF))))


(DEFMETHOD ZEROP     ((SELF NUMBER)) (COMMON-LISP:ZEROP SELF))
(DEFMETHOD ZEROP     ((SELF AMOUNT)) (COMMON-LISP:ZEROP (AMOUNT-VALUE SELF)))


(DEFMETHOD POSITIVEP ((SELF NUMBER)) (COMMON-LISP:<= 0 SELF))
(DEFMETHOD POSITIVEP ((SELF AMOUNT)) (COMMON-LISP:<= 0 (AMOUNT-VALUE SELF)))


(DEFMETHOD NEGATIVEP ((SELF NUMBER)) (COMMON-LISP:> 0 SELF))
(DEFMETHOD NEGATIVEP ((SELF AMOUNT)) (COMMON-LISP:> 0 (AMOUNT-VALUE SELF)))


(DEFMETHOD ROUND ((SELF REAL) &OPTIONAL (DIVISOR 1))
  (COMMON-LISP:ROUND SELF DIVISOR))


(DEFMETHOD ROUND ((SELF AMOUNT) &OPTIONAL (DIVISOR 1))
  (MAKE-AMOUNT :CURRENCY (AMOUNT-CURRENCY SELF)
               :VALUE (COMMON-LISP:ROUND (AMOUNT-VALUE SELF) DIVISOR)))


(DEFUN EURO-ROUND (MAGNITUDE CURRENCY)
  "
MAGNITUDE:  A REAL
CURRENCY:   The currency of the amount.
RETURN:     An integer in minor unit rounded according to the Euro rule."
  (LET ((ROUNDER (AREF #(1 1/10 1/100 1/1000 1/10000)
                       (CURRENCY-MINOR-UNIT CURRENCY))))
    (ROUND (+ MAGNITUDE (* (SIGNUM MAGNITUDE) (/ ROUNDER 10))) ROUNDER)))


(DEFUN EURO-VALUE-ROUND (VALUE)
  "
VALUE:      A REAL
CURRENCY:   The currency of the amount.
RETURN:     An integer in minor unit rounded according to the Euro rule."
    (ROUND (+ VALUE (* (SIGNUM VALUE) 1/10))));;EURO-VALUE-ROUND



;; (with-output-to-string (out)
;;   (dolist (*print-readably* '(t nil))
;;     (print (make-amount :currency (find-currency :EUR) :value 12345) out)))


(define-condition multi-currency-error (error)
  ((format-control   :initarg :format-control   :accessor format-control)
   (format-arguments :initarg :format-arguments :accessor format-arguments)
   (operation :initarg :operation :accessor multi-currency-error-operation)
   (amounts   :initarg :amounts   :accessor multi-currency-error-amounts))
  (:report (lambda (self stream)
             (let ((*print-pretty* nil))
             (format stream "~A: (~A  ~{~A~^, ~})~%~A"
                     (class-name (class-of self))
                     (multi-currency-error-operation self)
                     (multi-currency-error-amounts self)
                     (apply (function format) nil (format-control self)
                            (format-arguments self)))))))


(defun mcerror (operation amounts format-control &rest format-arguments)
  (ERROR 'multi-currency-error
         :operation operation :amounts amounts
         :format-control format-control
         :format-arguments format-arguments))

          

(defun types-of-arguments (args)
  (labels ((display
            (item)
            (cond ((symbolp item) (symbol-name item))
                  ((atom item) item)
                  (t (mapcar (function display) item)))))
    (mapcar (lambda (arg) (display (type-of arg))) args)))


(DEFMACRO MAKE-COMPARISON-METHOD (NAME OPERATOR)
  "
DO:     Generate a comparison method.
"
  `(DEFUN ,NAME (&REST ARGS)
     (COND
      ((EVERY (FUNCTION NUMBERP) ARGS) 
       (APPLY (FUNCTION ,OPERATOR) ARGS))
      ((EVERY (FUNCTION AMOUNTP) ARGS)
       (LET ((CURRENCY (FIND-CURRENCY (AMOUNT-CURRENCY (FIRST ARGS)))))
         (IF (EVERY (LAMBDA (X) (EQ CURRENCY (FIND-CURRENCY (AMOUNT-CURRENCY X))))
                    (CDR ARGS))
           (APPLY (FUNCTION ,OPERATOR)  (MAPCAR (FUNCTION AMOUNT-VALUE) ARGS))
           (mcerror ',name args  "Comparison not implemented yet."))))
      (T (mcerror ',name args  "Incompatible types: ~A"
                  (types-of-arguments args))))))


(MAKE-COMPARISON-METHOD <  COMMON-LISP:<)
(MAKE-COMPARISON-METHOD <= COMMON-LISP:<=)
(MAKE-COMPARISON-METHOD >  COMMON-LISP:>)
(MAKE-COMPARISON-METHOD >= COMMON-LISP:>=)
(MAKE-COMPARISON-METHOD =  COMMON-LISP:=)
(MAKE-COMPARISON-METHOD /= COMMON-LISP:/=)


(DEFUN + (&REST ARGS)
  "
DO:    A Generic addition with numbers or amounts.
"
  (setf args (remove 0 args
                     :key  (lambda (x) (if (typep x 'amount) (amount-value x) x))
                     :test (function equal)))
  (COND
   ((EVERY (FUNCTION NUMBERP) ARGS) 
    (APPLY (FUNCTION COMMON-LISP:+) ARGS))
   ((EVERY (FUNCTION AMOUNTP) ARGS)
    (LET ((CURRENCY (FIND-CURRENCY (AMOUNT-CURRENCY (FIRST ARGS)))))
      (IF (EVERY (LAMBDA (X) (EQ CURRENCY (FIND-CURRENCY (AMOUNT-CURRENCY X))))
                 (CDR ARGS))
        (MAKE-AMOUNT :CURRENCY CURRENCY 
                     :VALUE (APPLY (FUNCTION COMMON-LISP:+)
                                   (MAPCAR (FUNCTION AMOUNT-VALUE) ARGS)))
        (mcerror '+ args  "Addtion not implemented yet."))))
   (T   (mcerror '+ args  "Incompatible types: ~A" (types-of-arguments args)))))


(DEFUN - (&REST ARGS)
  "
DO:    A Generic substraction with numbers or amounts.
"
  (setf args (cons (car args)
                   (remove 0 (cdr args)
                           :key (lambda (x) (if (typep x 'amount) (amount-value x) x))
                           :test (function equal))))
  (COND
   ((EVERY (FUNCTION NUMBERP) ARGS)
    (APPLY (FUNCTION COMMON-LISP:-) ARGS))
   ((zerop (first args))
    (- (apply (function +) (rest args))))
   ((EVERY (FUNCTION AMOUNTP) ARGS)
    (LET ((CURRENCY (FIND-CURRENCY (AMOUNT-CURRENCY (FIRST ARGS)))))
      (IF (EVERY (LAMBDA (X) (EQ CURRENCY (FIND-CURRENCY (AMOUNT-CURRENCY X))))
                 (CDR ARGS))
        (MAKE-AMOUNT :CURRENCY CURRENCY 
                     :VALUE (APPLY (FUNCTION COMMON-LISP:-)
                                   (MAPCAR (FUNCTION AMOUNT-VALUE) ARGS)))
        (mcerror '- args  "Substraction not implemented yet."))))
   (T   (mcerror '- args  "Incompatible types: ~A" (types-of-arguments args)))))


(DEFUN * (&REST ARGS)
  "
DO:    A Generic multiplication with numbers or amounts.
"
  (IF (EVERY (FUNCTION NUMBERP) ARGS)
    (APPLY (FUNCTION COMMON-LISP:*) ARGS)
    (LET ((P (POSITION-IF (FUNCTION AMOUNTP) ARGS)))
      (COND
       ((OR (NULL P) (NOT (EVERY (LAMBDA (X) (OR (AMOUNTP X)(REALP X))) ARGS)))
        (mcerror '* args  "Incompatible types: ~A" (types-of-arguments args)))
       ((POSITION-IF (FUNCTION AMOUNTP) ARGS :START (1+ P))
        (mcerror '* args  "Cannot multiply moneys."))
       (T
        (MAKE-AMOUNT
         :CURRENCY (AMOUNT-CURRENCY (NTH P ARGS))
         :VALUE (EURO-VALUE-ROUND
                 (APPLY (FUNCTION COMMON-LISP:*)
                        (MAPCAR (LAMBDA (X) (IF (AMOUNTP X) (AMOUNT-VALUE X) X))
                                ARGS)))))))))


(DEFUN / (&REST ARGS)
  "
DO:    A Generic division with numbers or amounts.
"
  (COND
   ((EVERY (FUNCTION NUMBERP) ARGS)
    (APPLY (FUNCTION COMMON-LISP:/) ARGS))
   ((and (cadr args)
         (not (cddr args))              ; two arguments
         (amountp (first  args))
         (amountp (second args)))       ; both amounts
    ;; then return a number:
    (/ (amount-value (first args)) (amount-value (second args))))
   ((AND (AMOUNTP (CAR ARGS))
         (CDR ARGS) ;; cannot take the inverse of an amount!
         (EVERY (FUNCTION REALP) (CDR ARGS)))
    (MAKE-AMOUNT 
     :CURRENCY (AMOUNT-CURRENCY (CAR ARGS))
     :VALUE (EURO-VALUE-ROUND (APPLY (FUNCTION COMMON-LISP:/) 
                                     (AMOUNT-VALUE (CAR ARGS)) (CDR ARGS)))))
   (T (mcerror '/ args  "Incompatible types: ~A" (types-of-arguments args)))))



(EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE)

  (DEFUN CURRENCY-SYNTAX (STREAM CHAR INFIX)
    (DECLARE (IGNORE CHAR))
    (LET ((CURRENCY (OR INFIX *DEFAULT-CURRENCY*)))
      (SETF CURRENCY (FIND-CURRENCY CURRENCY))
      (UNLESS CURRENCY
        (mcerror 'currency-syntax (OR INFIX *DEFAULT-CURRENCY*)
                 "Invalid currency designator ~S" (OR INFIX *DEFAULT-CURRENCY*)))
      (ASSERT (<= 0 (CURRENCY-MINOR-UNIT CURRENCY) 4) ()
              "Unexpected  minor unit for currency: ~S" CURRENCY)
      (LET ((LEFT '())
            (RIGHT '())
            (DOT NIL)
            (SIGN 1))
        (LET ((CH (READ-CHAR STREAM NIL NIL)))
          (COND
           ((NULL CH))
           ((CHAR= CH (CHARACTER "-" )) (SETF SIGN -1))
           ((CHAR= CH (CHARACTER "+" )))
           (T (UNREAD-CHAR CH STREAM))))
        (LOOP FOR CH = (PEEK-CHAR NIL STREAM NIL NIL)
              WHILE (AND CH (DIGIT-CHAR-P CH))
              DO (PUSH (READ-CHAR STREAM) LEFT)
              FINALLY (SETF DOT (AND CH (CHAR= (CHARACTER ".") CH))))
        (WHEN (ZEROP (LENGTH LEFT))
          (mcerror 'currency-syntax currency "Missing an amount after #M"))
        (WHEN DOT
          (WHEN (ZEROP (CURRENCY-MINOR-UNIT CURRENCY))
            (mcerror 'currency-syntax currency
                     "There is no decimal point in ~A" (CURRENCY-NAME CURRENCY)))
          (READ-CHAR STREAM) ;; eat the dot
          (LOOP FOR CH = (PEEK-CHAR NIL STREAM NIL NIL)
                WHILE (AND CH (DIGIT-CHAR-P CH))
                DO (PUSH (READ-CHAR STREAM) RIGHT))
          (WHEN (< (CURRENCY-MINOR-UNIT CURRENCY) (LENGTH RIGHT))
            (mcerror 'currency-syntax currency
                     "Too many digits after the decimal point for ~A"
                     (CURRENCY-NAME CURRENCY))))
        (LOOP FOR I FROM (LENGTH RIGHT) BELOW (CURRENCY-MINOR-UNIT CURRENCY)
              DO (PUSH (CHARACTER "0") RIGHT))      
        (MAKE-AMOUNT
         :CURRENCY CURRENCY
         ;; (WITH-STANDARD-IO-SYNTAX
         ;;     (INTERN (CURRENCY-ALPHABETIC-CODE CURRENCY) "KEYWORD"))
         :VALUE (* SIGN (PARSE-INTEGER
                         (MAP 'STRING (FUNCTION IDENTITY)
                              (NREVERSE (NCONC RIGHT LEFT)))))
         ;;:divisor (AREF #(1 10 100 1000 10000)
         ;;   (CURRENCY-MINOR-UNIT CURRENCY))
         )))) ;;currency-syntax


  (DEFPARAMETER *CURRENCY-READTABLE* (COPY-READTABLE *READTABLE*)
    "The readtable used to read currencies.")


  (SET-DISPATCH-MACRO-CHARACTER  #\# #\M (FUNCTION CURRENCY-SYNTAX) 
                                 *CURRENCY-READTABLE*)

  (SET-DISPATCH-MACRO-CHARACTER  #\# #\M (FUNCTION CURRENCY-SYNTAX)
                                 *CURRENCY-READTABLE*)
  ) ;;eval-when

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
The mighty hunter
Returns with gifts of plump birds,
Your foot just squashed one.
From: Kent M Pitman
Subject: Re: How to xor?
Date: 
Message-ID: <uu0ipqwi7.fsf@nhplace.com>
Marcin 'Qrczak' Kowalczyk <······@knm.org.pl> writes:

> > The point is that often independent definitions invite what I'll call the
> > "modularity problem" where definitions look ok in isolation but surprising
> > effects happen in combination.  Similar to what
> >  (+ 1 2 "foo")
> > might do if we allowed + to be generic.
> 
> It should be an error: + should work only for numbers, string concatenation
> is a sufficiently different operation. But it doesn't mean that + shouldn't
> be generic.

I'm not sure "sufficiently different" is well-formed.

I have always felt like Group Theory held the answer to this terminological
problem, but I never studied it and so was terminologically impaired.  
Recently I started a teach yourself Teach Yourself Mathematical Groups and
now I'm armed with just a tiny bit of information, which makes me probably
dangerous because I have a new toy where I've not gotten done reading all the
instructions... ;)  [Maybe someone who does know better can set me onto the
right path if I've screwed up my analysis or terminology here.]

But the issue it seems to me is that you want generic functions (and
their range/domain data) to be something like a well-formed group
where possible--certainly you want them not to destroy the well-formedness
of any group that is there before making something generic.

So if + operates on some set S of data such that the usual rules of 
commutativity and associativity and whatnot apply and then suddenly you
add string concatenation, it's not the "differentness" of string 
concatenation that's the problem, it's the fact that you're defining
coercion from numbers to strings such that 
  (+ 1 2 "foo")
might mean
  (+ (+ 1 2) "foo") => "3foo"
or
  (+ 1 (+ 2 "foo")) => "12foo"

If it had been opaque values, (+ x y z), in a runtime-typed world like 
Lisp, this is just not acceptable because the compiler is no longer free
to optimize according to normal math optimization rules, turning
 (+ 1 2 x)
or
 (+ x 1 2)
into something simpler at compile time.  It has to compile all the arguments
for passing at runtime just in case + has some very particular order that
it must do things for reproducibility. Making this still clearer:
 (defun f (one three)
   (= (+ one 2 "three") (+ 1 2 three)))
 (f 1 "three")
is going to compile to something that will in all likelihood return NIL
because it's going to optimize it to:
 (defun f (one three)
   (= (+ one "2three") (+ 3 three)))
 (f 1 "three")

If it were the case that these properties were not disturbed, I'd have a lot
less concern about the sameness/differentness.

And I'm pretty sure if I thought hard that I'd I feel the same for the same
reasons about the recursive problem in
(length '(a b . "foo"))
That is, the problem is again that I PREFER the analysis that lets you optimize
(length (the list x))
and when you start saying that a cons could be part of some weird structure
that must be recursively analyzed, you take away the ability to do that,
requiring me to do runtime analysis that is usually unnecessary in the present
semantics, but that would be forced as an additional cost in the proposed
semantics.
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: How to xor?
Date: 
Message-ID: <87irz52iga.fsf@qrnik.zagroda>
Kent M Pitman <······@nhplace.com> writes:

>> It should be an error: + should work only for numbers, string
>> concatenation is a sufficiently different operation. But it doesn't
>> mean that + shouldn't be generic.
>
> I'm not sure "sufficiently different" is well-formed.
>
> I have always felt like Group Theory held the answer to this
> terminological problem, but I never studied it and so was
> terminologically impaired.

Using + for an arbitrary group operation presents a fundamental
problem: the group in question must be inferred from their elements.
You can't consider different group structures over the same set.

And there is no way to refer to the unit element in the same style,
as the meaning of a constant would have to depend on the context
(from languages I know only Haskell/Clean/Mercury can do that).

The natural design of generic groups in a programming language makes
the group an object which consists of the group operation, the unit
element, and the inverse. The group operation can be thus uncurried
to a ternary function, taking the group and the two elements.

Of course we don't want to explicitly say that we are adding integers.
So a generic group framework is one thing, and + is another: + must
be limited to some common cases where the group can be inferred from
the elements. Since it can't denote a fully generic group operation
anyway, restricting it to numbers should not be a big deal.

> So if + operates on some set S of data such that the usual rules of
> commutativity and associativity and whatnot apply and then suddenly
> you add string concatenation, it's not the "differentness" of string
> concatenation that's the problem, it's the fact that you're defining
> coercion from numbers to strings such that
>   (+ 1 2 "foo")
> might mean
>   (+ (+ 1 2) "foo") => "3foo"
> or
>   (+ 1 (+ 2 "foo")) => "12foo"

Yes, but even without the coercion it would be problematic: + couldn't
work for an arbitrary number of arguments, because (+) can't be 0 and
"" at the same time.

A binary (or non-empty) + would work, but it would be pointless:
I can't imagine a useful algorithm which would add numbers or
concatenate strings depending on the values given to it. An algorithm
which works for an arbitrary group should be expressed differently:
it should take the operation from the group structure instead of
inferring it from elements.

> And I'm pretty sure if I thought hard that I'd I feel the same for
> the same reasons about the recursive problem in
> (length '(a b . "foo"))
> That is, the problem is again that I PREFER the analysis that lets
> you optimize (length (the list x))

I never advocated length which would answer 5 to the above. Length
being a generic function can be implemented as efficiently as
currently: the compiler can assume that standard sequence types are
so common that they are checked first, and only as the last resort
the generic function is tried instead of signalling an error.

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/
From: Ivan Boldyrev
Subject: Re: How to xor?
Date: 
Message-ID: <2n0iq2-53r.ln1@ibhome.cgitftp.uiggm.nsc.ru>
On 9170 day of my life Kent M. Pitman wrote:
> One of the items on that list was the addition of xor.  As I recall
> what made it controversial was precisely this question of whether it
> should be a macro or special form (for consistency with AND and OR) or
> a function (because it somewhat accidentally is able to be, as an
> accident of how it is computed).

Why is it so important?  What is wrong if XOR is function but OR and
AND macros?  If macro/special form evaluates all their arguments
sequentially, why not implement it as a function?

Of course, you can perform more operations with a function than with a
special form (for example, MAPCAR and so on).  But it is only an
advantage IMHO.

-- 
Ivan Boldyrev

                Tragedy of programmers is that computer is wonderful toy
                            and programmers have to use it in their work.
From: Kent M Pitman
Subject: Re: How to xor?
Date: 
Message-ID: <ur7dygio8.fsf@nhplace.com>
Ivan Boldyrev <···············@cgitftp.uiggm.nsc.ru> writes:

> On 9170 day of my life Kent M. Pitman wrote:
> > One of the items on that list was the addition of xor.  As I recall
> > what made it controversial was precisely this question of whether it
> > should be a macro or special form (for consistency with AND and OR) or
> > a function (because it somewhat accidentally is able to be, as an
> > accident of how it is computed).
> 
> Why is it so important?  What is wrong if XOR is function but OR and
> AND macros?  If macro/special form evaluates all their arguments
> sequentially, why not implement it as a function?

It affects whether FUNCALL and APPLY work.

But it also affects regularity of design.  

But mostly, if implementations can differ, it's a portability problem.
THAT matters.  It should for that reason be clear.

> Of course, you can perform more operations with a function than with a
> special form (for example, MAPCAR and so on).  But it is only an
> advantage IMHO.

And so some implementations would do it if they can. Others will fail
to do it to "helpfully" remind you remember that it's not portable.
And in doing so, they will create the non-portability... ;)
From: Ivan Boldyrev
Subject: Re: How to xor?
Date: 
Message-ID: <1dccr2-esq.ln1@ibhome.cgitftp.uiggm.nsc.ru>
On 9173 day of my life Kent M. Pitman wrote:
>> Why is it so important?  What is wrong if XOR is function but OR and
>> AND macros?  If macro/special form evaluates all their arguments
>> sequentially, why not implement it as a function?
>
> It affects whether FUNCALL and APPLY work.

Yes, but it extends usage, but doesn't limit it.

> But it also affects regularity of design.  

(mapcar #'not some-list)

-- 
Ivan Boldyrev

                                                  Your bytes are bitten.
From: Christophe Rhodes
Subject: Re: How to xor?
Date: 
Message-ID: <sq64vfqdfv.fsf@cam.ac.uk>
Philippe Lorin <············@gmail.com> writes:

> CLisp seems to have XOR, but CMUCL and SBCL don't. Isn't there some
> standard function for this? 

No.

> Or am I wrong in wanting one?

You're not necessarily wrong, but note that AND and OR are macros, not
functions, because they short-circuit; also, they take arbitrary
numbers of arguments, not just two; so it's not clear that XOR
actually fits in with AND and OR.

> Background: I need to check that two values are either both null or
> both non-null, and not a mix of null and non-null. I'd like to write:
> (assert (not (xor (null v1) (null v2))))

(assert (if v1 v2 (not v2)))

This is easily wrapped up into a function if you feel the need.

Christophe
From: Frank Buss
Subject: Re: How to xor?
Date: 
Message-ID: <1wd9v8h0qhvfo.yiqi3y7vb5n2$.dlg@40tude.net>
Christophe Rhodes wrote:

> You're not necessarily wrong, but note that AND and OR are macros, not
> functions, because they short-circuit; also, they take arbitrary
> numbers of arguments, not just two; so it's not clear that XOR
> actually fits in with AND and OR.

a nice specification is given by the ext:xor function in CLisp:

| This function checks that exactly one of its arguments is non-NIL and,
| if this is the case, returns its value and index in the argument list
| as multiple values, otherwise returns NIL.

An implementation by me:

(defun xor (&rest list)
  (when list
    (destructuring-bind (first &rest rest) list
      (if first
          (when (every #'null rest) first)
        (apply #'xor rest)))))

I'm sure it could be done easier :-)

-- 
Frank Bu�, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Christophe Rhodes
Subject: Re: How to xor?
Date: 
Message-ID: <sqslyi1kq1.fsf@cam.ac.uk>
Frank Buss <··@frank-buss.de> writes:

> a nice specification is given by the ext:xor function in CLisp:
>
> | This function checks that exactly one of its arguments is non-NIL and,
> | if this is the case, returns its value and index in the argument list
> | as multiple values, otherwise returns NIL.

Why is this nice?

Christophe
From: Frank Buss
Subject: Re: How to xor?
Date: 
Message-ID: <4x2y8wobhl0h$.1say4nvpaje2r.dlg@40tude.net>
Christophe Rhodes wrote:

> Why is this nice?

because this is what xor means: the output is true, if exactly one of the
input is true. I think this could be used more often as the other
interpretation with counting the number of nils and checking for odd.

-- 
Frank Bu�, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Christopher C. Stacy
Subject: Re: How to xor?
Date: 
Message-ID: <uackp51rl.fsf@news.dtpq.com>
Frank Buss <··@frank-buss.de> writes:

> Christophe Rhodes wrote:
> 
> > Why is this nice?
> 
> because this is what xor means: the output is true, if exactly one of the
> input is true. I think this could be used more often as the other
> interpretation with counting the number of nils and checking for odd.

XOR is associative, which is why the n-ary (ODDP (COUNT NIL ..))
version is consistent with the two-arg version.  And since the
n-ary version doesn't cons, why wouldn't it be better?
From: Christophe Rhodes
Subject: Re: How to xor?
Date: 
Message-ID: <sqoe9528sc.fsf@cam.ac.uk>
Frank Buss <··@frank-buss.de> writes:

> Christophe Rhodes wrote:
>
>> Why is this nice?
>
> because this is what xor means: the output is true, if exactly one of the
> input is true. 

That is not what multiple argument xor means.

> I think this could be used more often as the other interpretation
> with counting the number of nils and checking for odd.

If you had any evidence for this, this might be a good reason, apart
from the fact that this interpretation is counter to the mathematical
definition.

Christophe
From: ··············@hotmail.com
Subject: Re: How to xor?
Date: 
Message-ID: <1121358014.540022.84070@g43g2000cwa.googlegroups.com>
Christophe Rhodes wrote:
> Frank Buss <··@frank-buss.de> writes:
>
> > because this is what xor means: the output is true, if exactly one of the
> > input is true.
>
> That is not what multiple argument xor means.

I'm not sure there is a completely unambigous definition for multiple
argument "eXclusive OR."

"One and only one" is an interpretation that emphasizes the "exclusive"
distinction.

A XOR B XOR C is another interpretation, which emphasizes the "addition
mod 2" definition.

For two arguments, these definitions are equivalent; for multiple
arguments, they obviously differ.
From: Frank Buss
Subject: Re: How to xor?
Date: 
Message-ID: <1tspj4xqwhc1u$.x7wliqwb7yjf.dlg@40tude.net>
Christophe Rhodes wrote:

> That is not what multiple argument xor means.

yes, I know this and logxor works like the mathematical definition, but for
the logical counterpart I think the other definition is more useful. But
perhaps it should be renamed (something like "only-one"), to avoid
confusion.

>> I think this could be used more often as the other interpretation
>> with counting the number of nils and checking for odd.
> 
> If you had any evidence for this, this might be a good reason, apart
> from the fact that this interpretation is counter to the mathematical
> definition.

You should ask the CLisp people :-) but a far-fetched example: You have 4
switches for controlling back/forward of 2 motors, but you are not allowed
to turn on back and forward of one motor at the same time, because it is a
push-pull circuit and this would result in a short circuit and you are not
allowed to turn on two motors at once, because this would cause too much
power consumption:

(defun control-motors (up down left right)
  (when (xor up down left right)
    (set-relay 1 up)
    (set-relay 2 down)
    (set-relay 3 left)
    (set-relay 4 right)))

-- 
Frank Bu�, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Christophe Rhodes
Subject: Re: How to xor?
Date: 
Message-ID: <sq64vdes4m.fsf@cam.ac.uk>
Frank Buss <··@frank-buss.de> writes:

> Christophe Rhodes wrote:
>> [Frank Buss]
>>> I think this could be used more often as the other interpretation
>>> with counting the number of nils and checking for odd.
>> 
>> If you had any evidence for this, this might be a good reason, apart
>> from the fact that this interpretation is counter to the mathematical
>> definition.
>
> You should ask the CLisp people :-) but a far-fetched example

I don't deny that exactly-one-true has a use; I want to know why you
think it is used more often than odd-number-true, and whether that has
any basis in reality.

Christophe
From: Frank Buss
Subject: Re: How to xor?
Date: 
Message-ID: <1qiaopdq6ryzv$.use2qkguyqd3.dlg@40tude.net>
Frank Buss wrote:

>| This function checks that exactly one of its arguments is non-NIL and,
>| if this is the case, returns its value and index in the argument list
>| as multiple values, otherwise returns NIL.
> 
> An implementation by me:

I forgot the index, so some standard loop magic:

(defun xor (&rest list)
  (when list
    (loop for (first . rest) on list
          for i = 0 then (1+ i) do 
          (if first
              (when (every #'null rest) (return (values first i)))))))

-- 
Frank Bu�, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Louis Theran
Subject: Re: How to xor?
Date: 
Message-ID: <1121302879.656431.280310@o13g2000cwo.googlegroups.com>
Frank Buss wrote:
> Frank Buss wrote:

> I forgot the index, so some standard loop magic:

If you are already using loop, save typing:

(defun xor (&rest args)
  (loop for (a . d) on args
        for i from 0
        until a
        finally (return (and (every 'null d) (values a i)))))

Normally, with the name xor, I would expect something like:

(defun xor (&rest args)
  (oddp (count nil args :key 'null)))

^L
From: Michiel Borkent
Subject: Re: How to xor?
Date: 
Message-ID: <1121332300.271123.113550@g47g2000cwa.googlegroups.com>
Hi,

#|
Frank Buss's wrote:

(defun xor (&rest list)
  (when list
    (loop for (first . rest) on list
          for i = 0 then (1+ i) do
          (if first
              (when (every #'null rest) (return (values first i)))))))
|#

How is this XOR? I don't get this definition.

(xor t t .... t) returns t,i, where i is the index of the last
argument.

For clarity, I use the definition of XOR with 2 arguments: only one and
at least one of its arguments is t, then it returns t, else nil.

What definition of multiple argument XOR do you use?

Greetings,
Michiel
From: Pascal Bourguignon
Subject: Re: How to xor?
Date: 
Message-ID: <87mzopgwii.fsf@thalassa.informatimago.com>
"Michiel Borkent" <··············@gmail.com> writes:
> For clarity, I use the definition of XOR with 2 arguments: only one and
> at least one of its arguments is t, then it returns t, else nil.
>
> What definition of multiple argument XOR do you use?

a xor b xor c = ( a xor b ) xor c = a xor ( b xor c )

(defun xor (&rest args)
  (flet ((xor2 (a b) (not (eq (not a) (not b)))))
    (cond ((null args)  t)
          ((null (cdr args)) (car args))
          (t (apply (function xor) (xor2 (car args) (cadr args)) (cddr args))))))


(insert (karnaugh '(a b) (list (cons (quote xor) (function xor))) 
                  '("NO" . "YES") '("NO" . "YES")))

+-----+-----+-----+
|  a  |  b  | xor |
+-----+-----+-----+
| YES | YES |  NO |
| YES |  NO | YES |
|  NO | YES | YES |
|  NO |  NO |  NO |
+-----+-----+-----+

(insert (karnaugh '(a b c) (list (cons (quote xor) (function xor))) 
                  '("NO" . "YES") '("NO" . "YES")))
+-----+-----+-----+-----+
|  a  |  b  |  c  | xor |
+-----+-----+-----+-----+
| YES | YES | YES | YES |
| YES | YES |  NO |  NO |
| YES |  NO | YES |  NO |
| YES |  NO |  NO | YES |
|  NO | YES | YES |  NO |
|  NO | YES |  NO | YES |
|  NO |  NO | YES | YES |
|  NO |  NO |  NO |  NO |
+-----+-----+-----+-----+


(insert (karnaugh '(a b c d) (list (cons (quote xor) (function xor))) 
                  '("NO" . "YES") '("NO" . "YES")))
+-----+-----+-----+-----+-----+
|  a  |  b  |  c  |  d  | xor |
+-----+-----+-----+-----+-----+
| YES | YES | YES | YES |  NO |
| YES | YES | YES |  NO | YES |
| YES | YES |  NO | YES | YES |
| YES | YES |  NO |  NO |  NO |
| YES |  NO | YES | YES | YES |
| YES |  NO | YES |  NO |  NO |
| YES |  NO |  NO | YES |  NO |
| YES |  NO |  NO |  NO | YES |
|  NO | YES | YES | YES | YES |
|  NO | YES | YES |  NO |  NO |
|  NO | YES |  NO | YES |  NO |
|  NO | YES |  NO |  NO | YES |
|  NO |  NO | YES | YES |  NO |
|  NO |  NO | YES |  NO | YES |
|  NO |  NO |  NO | YES | YES |
|  NO |  NO |  NO |  NO |  NO |
+-----+-----+-----+-----+-----+


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

There is no worse tyranny than to force a man to pay for what he does not
want merely because you think it would be good for him. -- Robert Heinlein
From: Frank Buss
Subject: Re: How to xor?
Date: 
Message-ID: <4fofshi9lvau.1faq9ynhsflsc.dlg@40tude.net>
Philippe Lorin wrote:

> Background: I need to check that two values are either both null or both 
> non-null, and not a mix of null and non-null. I'd like to write:
> (assert (not (xor (null v1) (null v2))))

what about (eq (null v1) (null v2)) ?

-- 
Frank Bu�, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Philippe Lorin
Subject: Re: How to xor?
Date: 
Message-ID: <42d500c8$0$15020$626a14ce@news.free.fr>
Frank Buss wrote:
> what about (eq (null v1) (null v2)) ?

This one is nice, it conveys the intention perfectly. Thanks!
From: ······@gmail.com
Subject: Re: How to xor?
Date: 
Message-ID: <1121289297.806264.152960@o13g2000cwo.googlegroups.com>
Philippe Lorin wrote:
> I'm looking for a XOR to use like I use AND and OR. But the only things
> I found in the CLHS are:
>    BIT-XOR, which works on bit arrays
>    BOOLE-XOR, which, when used with BOOLE, works on integers
>    LOG-XOR, which works on integers
>
> CLisp seems to have XOR, but CMUCL and SBCL don't. Isn't there some
> standard function for this? Or am I wrong in wanting one?
>
> Background: I need to check that two values are either both null or both
> non-null, and not a mix of null and non-null. I'd like to write:
> (assert (not (xor (null v1) (null v2))))

Assuming v1 and v2 are boolean:
(xor v1 v2) == (not (eq v1 v2))
(xnor v1 v2) == (eq v1 v2)

So, in your case I would write:
(assert (eq (null v1) (null v2)))

As #'AND and #'OR are both macros that take any number of arguments,
I'm not sure that XOR really does fit in with them.  However, it is
trivially implementable as a function because it must evaluate all of
its arguments.

Justin Dubs