Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

predicate

93 views
Skip to first unread message

TheFlyingDutchman

unread,
Feb 28, 2011, 2:10:16 AM2/28/11
to
From the Common Lisp Hyperspec:

predicate n. a function that returns a generalized boolean as its
first value.

Is that really supposed to be "generalized boolean" (nil and not-nil)
and not boolean (nil and t)?

vanekl

unread,
Feb 28, 2011, 3:17:42 AM2/28/11
to

Dutchman who Flies:
It makes sense to use generalized Booleans because they serve two purposes:
1) non-nils evaluate to 't'; and, at the same time,
2) non-nils do not destroy information.

It's common to use the result of a predicate if it evaluates to true, and the most efficient way to make that happen is to pass the value that causes the predicate to evaluate to true back to the calling function.

An example will illustrate. The 'member' function doesn't simply return 't' if it finds its key; it does this so that you can also get associated information. If the behavior of 'member' were coded more prosaically (by returning 't') then two member functions would have to be written to do the job of one, or the member function would have to have an additional flag argument indicating whether 't' should be returned or the non-nil found value. [Yes, I know 'member' is not a pure predicate function but CL is largely functional and there is large overlap between predicates and functions.]

Keep in mind that returning the non-nil value can be as efficient as returning 't'. In the above example, an (obfuscated) pointer is returned which takes up the same amount of space as 't' (i.e., one register). So there is no penalty in using this design.

I would be more befuddled if this design decision were *not* chosen.

Tim Bradshaw

unread,
Feb 28, 2011, 5:56:17 AM2/28/11
to
On 2011-02-28 07:10:16 +0000, TheFlyingDutchman said:

> Is that really supposed to be "generalized boolean" (nil and not-nil)
> and not boolean (nil and t)?

Yes, it is. There is often useful information which can be returned in
the case when the result is non-NIL.

Pascal J. Bourguignon

unread,
Feb 28, 2011, 7:10:50 AM2/28/11
to
Tim Bradshaw <t...@tfeb.org> writes:

However, a lot of predicates are defined in CL to return a generalized
boolean without specifying anything more specific.

For these predicates, a conforming program cannot count on any specific
non NIL value.

IMO, generalized boolean should be seen, in general, as an optimization
device. They allow implementations to generate very efficient code for
predicates, notably on architectures that are register-deprived, since
the accumulator will generally contain a non NIL value when it should
return true.

More over, for operators such as <, an implementation that returns one
of the extrema parameters can use this fact for further optimize
expressions using it such as: (if (< a b) a b)
if (< a b) returns in the accumulator either a or b, then either branch
if the if form can be empty.

--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.

Tim Bradshaw

unread,
Feb 28, 2011, 7:33:43 AM2/28/11
to
On 2011-02-28 12:10:50 +0000, Pascal J. Bourguignon said:

> However, a lot of predicates are defined in CL to return a generalized
> boolean without specifying anything more specific.
>
> For these predicates, a conforming program cannot count on any specific
> non NIL value.

I'm unclear why this could ever matter.

> More over, for operators such as <, an implementation that returns one
> of the extrema parameters can use this fact for further optimize
> expressions using it such as: (if (< a b) a b)
> if (< a b) returns in the accumulator either a or b, then either branch
> if the if form can be empty.


(if (< a b) a b)) is otherwise known as (min a b)

Pascal J. Bourguignon

unread,
Feb 28, 2011, 7:42:07 AM2/28/11
to
Tim Bradshaw <t...@tfeb.org> writes:

Yes. So the standard allow for efficient implementations of MIN :-)

Try to understand the wider message.

Tim Bradshaw

unread,
Feb 28, 2011, 8:06:44 AM2/28/11
to
On 2011-02-28 12:42:07 +0000, Pascal J. Bourguignon said:

> Yes. So the standard allow for efficient implementations of MIN :-)

As it would if < was specified to return T/NIL, since a CL
implementation is free to use any tricks it wants in the implementation
of the function.

>
> Try to understand the wider message.

I have no idea what the wider message is. Obviously not insisting on
T/NIL but allowing generalised booleans is a good thing, even when the
specific non-NIL value is not specified.

Norbert_Paul

unread,
Feb 28, 2011, 11:24:34 AM2/28/11
to

Yes. So you can use that equivalent definition:

prediacte n, a function the name of which either ends with the
letter p or ends with some other character.

Norbert_Paul

unread,
Feb 28, 2011, 11:29:34 AM2/28/11
to
Tim Bradshaw wrote:
> On 2011-02-28 12:10:50 +0000, Pascal J. Bourguignon said:
> (if (< a b) a b)) is otherwise known as (min a b)

Well there might be a subtle difference:

min is defined for reals and returns reals
< is defined for numbers and can be used for complex numbers, too.

But that might not be a difference, and I might be unaware of other
subleties according to which that subtle difference does not exist.

Björn Lindberg

unread,
Feb 28, 2011, 11:35:07 AM2/28/11
to
Norbert_Paul <norbertpau...@yahoo.com> writes:

A function returning no values would match your definition but not the
one of the spec.


Björn Lindberg

Tim Bradshaw

unread,
Feb 28, 2011, 12:05:29 PM2/28/11
to
On 2011-02-28 16:29:34 +0000, Norbert_Paul said:

> < is defined for numbers and can be used for complex numbers, too.

Is that correct? The standard certainly says that, but it seems to me
a bit mad. It doesn't really give any semantics for complex numbers,
and I'm not sure what the semantics should be (I think comparing
absolute values would be wrong - perhaps comparing real parts?).

Norbert_Paul

unread,
Feb 28, 2011, 12:17:08 PM2/28/11
to

Well, this could be disputed: A function returning no value returns nil as
its primary value. I know that "first value" and "primary value" are not
exactly the same, but this doesn't make a difference in practice.

Suggestion:

predicate n, a function that returns a value and the name of which ends
with the character #\P or some other character.

Paul Rubin

unread,
Feb 28, 2011, 12:21:56 PM2/28/11
to
"Pascal J. Bourguignon" <p...@informatimago.com> writes:
> IMO, generalized boolean should be seen, in general, as an optimization
> device. They allow implementations to generate very efficient code for
> predicates, notably on architectures that are register-deprived, since
> the accumulator will generally contain a non NIL value when it should
> return true.

Lots of times I'd hope the compiler could do the optimization even if
the function is specified to return an actual bool.

Norbert_Paul

unread,
Feb 28, 2011, 12:22:54 PM2/28/11
to

Yes.

CLHS on <:
Exceptional Situations:
Might signal type-error if some argument is not a real.
Might signal arithmetic-error if otherwise unable to fulfill its contract.

I guess, the imaginary part must then be zero.

TheFlyingDutchman

unread,
Feb 28, 2011, 12:44:21 PM2/28/11
to
> Suggestion:
>
> predicate n, a function that returns a value and the name of which ends
> with the character #\P or some other character.- Hide quoted text -
>

When I asked the question I was just thinking about the fact that
other Lisps (I believe Clojure, Emacs Lisp, Scheme and ISLisp all
qualify) seem to reserve the term "predicate" for functions which
return either t or nil (or #t or #f or true or false). But as you
point out, since every value returned falls into the set of values of
a "generalized boolean" (nil or not-nil) it could be argued that every
Common Lisp function is a predicate. Or you could argue that any
function that could potentially return nil and any other value is a
predicate such as car, cdr, and even - (append). If "returns t or nil"
is not part of the definition then it seems like "makes a decision as
to whether some condition is true or false" should be.

Tim Bradshaw

unread,
Feb 28, 2011, 12:47:36 PM2/28/11
to
On 2011-02-28 17:22:54 +0000, Norbert_Paul said:

> CLHS on <:
> Exceptional Situations:
> Might signal type-error if some argument is not a real.
> Might signal arithmetic-error if otherwise unable to fulfill its contract.
>
> I guess, the imaginary part must then be zero.

When playing with this I discovered something I never knew: #c(5 0) and
5 are the same (EQL) object and are RATIONALs.

Raymond Wiker

unread,
Feb 28, 2011, 2:43:36 PM2/28/11
to
Tim Bradshaw <t...@tfeb.org> writes:

Case in point: #'digit-char-p. I just remembered this function this
morning, after having written my own code for turning a hex character
into its corresponding integer value. A simple call to digit-char-p is
*obviously* better than the 8 lines of nicely-formatted, conventional
and ultimately completely unneccessary code that I came up with.

Edmunds Cers

unread,
Feb 28, 2011, 2:43:51 PM2/28/11
to
TheFlyingDutchman <zzbb...@aol.com> writes:

> But as you point out, since every value returned falls into the set of
> values of a "generalized boolean" (nil or not-nil) it could be argued
> that every Common Lisp function is a predicate.

This stems directly from CL treating NIL as false and any other value as
true. Since a predicate is just a function that returns a truth value,
every CL function *can* be seen as a predicate. But I don't see anything
wrong with that. (C does the same, only there 0 is the false value.)

> If "returns t or nil" is not part of the definition then it seems like
> "makes a decision as to whether some condition is true or false"
> should be.

But this is always an implication anyway, for any function capable of
returning NIL.

--
A change in perspective is worth 80 IQ points. --- Alan Kay

Marco Antoniotti

unread,
Feb 28, 2011, 4:24:37 PM2/28/11
to

Well. I'd bet it *will* serve a purpose. You can post the code, and
come back to the thread 5 or 6 years from now to see what WJ has come
up with in Ruby, clojure, guile, scheme, or SLDJ :)

Cheers
--
Marco

Peter Keller

unread,
Feb 28, 2011, 4:45:15 PM2/28/11
to
Marco Antoniotti <mar...@gmail.com> wrote:

> On Feb 28, 8:43?pm, Raymond Wiker <r...@RAWMBP-2.local> wrote:
>> Tim Bradshaw <t...@tfeb.org> writes:
>> > On 2011-02-28 07:10:16 +0000, TheFlyingDutchman said:
>>
>> >> Is that really supposed to be "generalized boolean" (nil and not-nil)
>> >> and not boolean (nil and t)?
>>
>> > Yes, it is. ?There is often useful information which can be returned

>> > in the case when the result is non-NIL.
>>
>> Case in point: #'digit-char-p. I just remembered this function this
>> morning, after having written my own code for turning a hex character
>> into its corresponding integer value. A simple call to digit-char-p is
>> *obviously* better than the 8 lines of nicely-formatted, conventional
>> and ultimately completely unneccessary code that I came up with.
>
> Well. I'd bet it *will* serve a purpose. You can post the code, and
> come back to the thread 5 or 6 years from now to see what WJ has come
> up with in Ruby, clojure, guile, scheme, or SLDJ :)

I bet in a hundred years, some data archeologist will find his
posts and it'll be like a rosetta stone. "Oh, so THAT's how Ruby
was written!"

To which the next logical comment would be. "Eeewww".

:)

-pete

P.S. Try the veal.

Barry Margolin

unread,
Feb 28, 2011, 8:33:06 PM2/28/11
to
In article
<d0bf0d60-469b-4713...@w7g2000pre.googlegroups.com>,
TheFlyingDutchman <zzbb...@aol.com> wrote:

"predicate" is clearly a human-oriented concept, not a machine-oriented
one. The definition is trying to use the technical term (boolean) to
indicate that the function answers a true/false question. And then adds
the "generalized" qualifier to clarify that true is not necessarily
indicated using T.

MEMBER is both a predicate and a searching function. You can use it
simply for its truth value, e.g. (if (member ...) ...), or you can use
it to find the tail of the list beginning with the element being
searched for.

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***

Rob Warnock

unread,
Feb 28, 2011, 9:39:26 PM2/28/11
to
Tim Bradshaw <t...@tfeb.org> wrote:
+---------------

| When playing with this I discovered something I never knew:
| #c(5 0) and 5 are the same (EQL) object and are RATIONALs.
+---------------

Ah, yezz... Good ol' "12.1.5.3 Rule of Canonical Representation for
Complex Rationals":

If the result of any computation would be a complex number whose
real part is of type rational and whose imaginary part is zero,
the result is converted to the rational which is the real part.

But while we are reviewing such minutia, it's good to remember that
this applies *only* if the real part is a rational, *not* a float:

This rule does not apply to complex numbers whose parts are floats.
For example, #C(5 0) and 5 are not different objects in Common Lisp
(they are always the same under EQL); #C(5.0 0.0) and 5.0 are always
different objects in Common Lisp they are never the same under EQL,
although they are the same under equalp and =).

And as "Function COMPLEX" points out, even a zero float imaginary part
is enough to trigger contagion on the entire number:

Examples:
...
(complex 3/2 0.0) => #C(1.5 0.0)

Similarly:

(read-from-string "#c(0 0.0)") => #C(0.0 0.0)
(read-from-string "#c(0.0 0)") => #C(0.0 0.0)

-Rob

-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <http://rpw3.org/>
San Mateo, CA 94403

TheFlyingDutchman

unread,
Feb 28, 2011, 11:00:03 PM2/28/11
to
> "predicate" is clearly a human-oriented concept, not a machine-oriented
> one.  The definition is trying to use the technical term (boolean) to
> indicate that the function answers a true/false question.  And then adds
> the "generalized" qualifier to clarify that true is not necessarily
> indicated using T.
>
> MEMBER is both a predicate and a searching function.  You can use it
> simply for its truth value, e.g. (if (member ...) ...), or you can use
> it to find the tail of the list beginning with the element being
> searched for.
>
Am I wrong in saying that in Scheme documentation they would not refer
to a function like "member" as a predicate?

George Neuner

unread,
Mar 1, 2011, 12:56:15 AM3/1/11
to
On Mon, 28 Feb 2011 20:00:03 -0800 (PST), TheFlyingDutchman
<zzbb...@aol.com> wrote:

>> [CL] MEMBER is both a predicate and a searching function.


>>
>Am I wrong in saying that in Scheme documentation they would not refer
>to a function like "member" as a predicate?

I think that's probably correct ... Scheme predicates return #T or #F,
but member returns a list if the object is found. It is potentially
confusing, though, because member returns #F if the object is not
found.

George

Pascal J. Bourguignon

unread,
Mar 1, 2011, 1:02:56 AM3/1/11
to
George Neuner <gneu...@comcast.net> writes:

Yes, at least, Common Lisp is consistent here, and returns () when the
element is not found... But in scheme, () is true.

Marco Antoniotti

unread,
Mar 1, 2011, 3:22:29 AM3/1/11
to
On Feb 28, 10:45 pm, Peter Keller <psil...@cs.wisc.edu> wrote:

:) :)

>
> -pete
>
> P.S. Try the veal.

:) I'll do. But I don't understand the reference :)

Pascal J. Bourguignon

unread,
Mar 1, 2011, 4:20:40 AM3/1/11
to
Marco Antoniotti <mar...@gmail.com> writes:

I'm not sure either, but "Fawlty Towers" has an episode dealing
intensively with veal.

http://en.wikipedia.org/wiki/Basil_the_Rat
http://www.youtube.com/watch?v=VPP4P3gKGss

Warning: watch it only of a floor with a thick rug under you.

Tim Bradshaw

unread,
Mar 1, 2011, 4:33:17 AM3/1/11
to
On 2011-03-01 04:00:03 +0000, TheFlyingDutchman said:

> Am I wrong in saying that in Scheme documentation they would not refer
> to a function like "member" as a predicate?

R^6RS says:

"A predicate is a procedure that always returns a boolean value (#t or #f)."

(so, no, you are not wrong)

Raffael Cavallaro

unread,
Mar 1, 2011, 9:09:19 AM3/1/11
to
On 2011-03-01 03:22:29 -0500, Marco Antoniotti said:

>>
>> P.S. Try the veal.
>
> :) I'll do. But I don't understand the reference :)

People doing acts that approximate stand up comedy at mediocre hotels,
restaurants, or lounges often add the tag line "try the veal" because
in such restaurant acts the proprietor often has the comedian push
certain menu items. The fuller version is more like: "Thank you! I'll
be here all week; try the veal."

So the suggestion is that your line:

"I'd bet it *will* serve a purpose. You can post the code, and
come back to the thread 5 or 6 years from now to see what WJ has come
up with in Ruby, clojure, guile, scheme, or SLDJ :)"

is akin to a stand up comic's one-liner.

BTW, one also comonly hears "ba-dum ching" or sees <rimshot> [1] in the
same contexts, because the drummer in the band at these sorts of places
often plays a rimshot to punctuate each joke.

warmest regards,

Ralph
[1] apparently more properly referred to as a "sting" - who knew?
<http://en.wikipedia.org/wiki/Rimshot>
<http://en.wikipedia.org/wiki/Sting_(percussion)>
--
Raffael Cavallaro

Marco Antoniotti

unread,
Mar 1, 2011, 9:54:07 AM3/1/11
to
On Mar 1, 3:09 pm, Raffael Cavallaro

Ok. I am a bad stand-up comedian :{

Cheers
--
Marco

Raffael Cavallaro

unread,
Mar 1, 2011, 11:25:31 AM3/1/11
to
On 2011-03-01 09:54:07 -0500, Marco Antoniotti said:

> Ok. I am a bad stand-up comedian :{

Not bad - at least I thought your joke was very funny!

Nowadays the whole meme has been appropriated even by good comedians
when telling any joke that has the structure or format commonly used by
such lounge comedians - any pun or quick one-liner.

warmest regards,

Ralph

--
Raffael Cavallaro

Marco Antoniotti

unread,
Mar 1, 2011, 12:22:57 PM3/1/11
to
On Mar 1, 5:25 pm, Raffael Cavallaro

Thanks :) I will be off to watch some "Big Bang Theory" :)

Cheers
--
Marco

George Neuner

unread,
Mar 1, 2011, 2:03:07 PM3/1/11
to
On Tue, 01 Mar 2011 07:02:56 +0100, "Pascal J. Bourguignon"
<p...@informatimago.com> wrote:

>George Neuner <gneu...@comcast.net> writes:
>
>> On Mon, 28 Feb 2011 20:00:03 -0800 (PST), TheFlyingDutchman
>> <zzbb...@aol.com> wrote:
>>
>>>> [CL] MEMBER is both a predicate and a searching function.
>>>>
>>>Am I wrong in saying that in Scheme documentation they would not refer
>>>to a function like "member" as a predicate?
>>
>> I think that's probably correct ... Scheme predicates return #T or #F,
>> but member returns a list if the object is found. It is potentially
>> confusing, though, because member returns #F if the object is not
>> found.
>
>Yes, at least, Common Lisp is consistent here, and returns () when the
>element is not found... But in scheme, () is true.

Personally, I think CL handles booleans wrong too. I really like the
notion that boolean is a distinct type with distinct true and false
values ... it occasionally forces an explicit test to satisfy the
compiler, but it avoids a whole host of semantic problems in the most
common case of dealing with binary YES/NO logic.

I just wish Scheme actually followed through on its own design advice.

IMO, functions such as member should multiple return the boolean
result, found or not found, and the list, empty or not. But the basic
library functions - in Scheme at least - predate (values). Of course
R6RS is mostly incompatible with previous versions anyway, so IMO the
committee should have taken the opportunity to redefine stupid library
functions.

George

Don Geddis

unread,
Mar 2, 2011, 1:04:09 AM3/2/11
to
George Neuner <gneu...@comcast.net> wrote on Tue, 01 Mar 2011:
> I really like the notion that boolean is a distinct type with distinct
> true and false values

OK, that sounds reasonable at first glance...

> ... it occasionally forces an explicit test to the compiler

Yeah, that. (See below.)

> but it avoids a whole host of semantic problems in the most common
> case of dealing with binary YES/NO logic.

Can you provide some more details about what "semantic problems" you
have in mind?

Cause mostly, it just makes me think of this wonderful poem from a
couple of decades ago (appended below).

-- Don
_______________________________________________________________________________
Don Geddis http://don.geddis.org/ d...@geddis.org

-------------------------------------------------------------------------------
A Short Ballad Dedicated to the Growth of Programs

Date: 28 Jan 86 06:22:59 EST (Tue)
From: ram@yale-ring
Subject: Of growing code and diminishing hacks...
Sender: ram%yale-ring%arpa...@edu.mit.lcs.mc
To: t-disc...@arpa.yale, sch...@arpa.mit-mc

A SHORT BALLAD DEDICATED TO THE GROWTH OF PROGRAMS
==================================================
by
Ashwin Ram

This is a tale of a sorry quest
To master pure code at the T guru's behest
I enrolled in a class that appealing did seem
For it promised to teach fine things like T3 and Scheme

The first day went fine; we learned of cells
And symbols and lists and functions as well
Lisp I had mastered and excited was I
For to master T3 my hackstincts did cry

I sailed through the first week with no problems at all
And I even said "closure" instead of "function call"
Then said the master that ready were we
To start real hacking instead of simple theory

Will you, said he, write me a function please
That in lists would associate values with keys
I went home and turned on my trusty Apollo
And wrote a function whose definition follows:

(cdr (assq key a-list))

A one-liner I thought, fool that I was
Just two simple calls without a COND clause
But when I tried this function to run
CDR didn't think that NIL was much fun

So I tried again like the good King of yore
And of code I easily generated some more:

(cond ((assq key a-list) => cdr))

It got longer but purer, and it wasn't too bad
But then COND ran out and that was quite sad

Well, that isn't hard to fix, I was told
Just write some more code, my son, be bold
Being young, not even a moment did I pause
I stifled my instincts and added a clause

(cond ((assq key a-list) => cdr)
(else nil))

Sometimes this worked and sometimes it broke
I debugged and prayed and even had a stroke
Many a guru tried valiantly to help
But undefined datums their efforts did squelch.

I returneth once more to the great sage of T
For no way out of the dilemma I could see
He said it was easy -- more lines must I fill
with code, for FALSE was no longer NIL.

(let ((val (assq key a-list)))
(cond (val (cdr val))
(else nil)))

You'd think by now I might be nearing the end
Of my ballad which seems bad things to portend
You'd think that we could all go home scot-free
But COND eschewed VAL; it wanted #T

So I went back to the master and appealed once again
I said, pardon me, but now I'm really insane
He said, no you're not really going out of your head
Instead of just VAL, you must use NOT NULL instead

(let ((val (assq key a-list)))
(cond ((not (null? val)) (cdr val))
(else nil)))

My song is over and I'm going home to bed
With this ineffable feeling that I've been misled
And just in case my point you have missed
Somehow I preferred (CDR (ASSQ KEY A-LIST))

:-)
==================================================
-------------------------------------------------------------------------------

George Neuner

unread,
Mar 8, 2011, 4:35:29 PM3/8/11
to
On Tue, 01 Mar 2011 22:04:09 -0800, Don Geddis <d...@geddis.org> wrote:

>George Neuner <gneu...@comcast.net> wrote on Tue, 01 Mar 2011:
>> I really like the notion that boolean is a distinct type with distinct
>> true and false values
>
>OK, that sounds reasonable at first glance...
>
>> ... it occasionally forces an explicit test to the compiler
>
>Yeah, that. (See below.)
>
>> but it avoids a whole host of semantic problems in the most common
>> case of dealing with binary YES/NO logic.
>
>Can you provide some more details about what "semantic problems" you
>have in mind?

Hi Don.

Sorry for the delay - I've was traveling most of last week and haven't
been keeping up with the newsgroups.


I'm sure you've heard this before, so if you think I'm cracked, don't
worry ... you're not the only one 8-)

Understand that my feelings about "generalized booleans" are, shall we
say, "enhanced" by near daily exposure to the bad implementation in
C(++), but I have long past the point where even Lisp's (fairly well)
sanitized version irritates me. It's become one of my big peeves
regarding language design - largely because I see the crop of wannabe
languages (Ruby,Python,etc.) making the same mistakes all over again.

My opinions also are shaped by many years working on embedded and high
availability systems where software crashes simply are not tolerated.
Companies losing many (tens of) thousands of dollars per minute to
downtime are not shy about badmouthing and suing the equipment vendors
- if the mission critical system you provided isn't working, forget
software bugs ... the computer had better be on fire.

So I am all about robust, correct code and through those glasses I see
generalized booleans as 1% programmer convenience, 99% programmer
pitfall. As far as I'm concerned, treating arbitrary values as
boolean just provides opportunities for an unwary programmer to
introduce control logic errors by coding an incorrect test.

[And yes, the problem really is the making of logic errors rather than
semantic ones, but these particular logic errors are only possible
because of the semantics of the conditional form.]

Clearly it is shorter to type "(if obj ..." - or whatever conditional
form floats your boat - then the equivalent "(if (not (eq obj nil))
...". The shorthand is fine if that is exactly what you meant for the
test to be. Lisp - or at least CL - defines what will happen in this
circumstance. It's less clear what will happen when the test involves
a function call or macro expansion: things like the SETF macro can't
be relied on to return anything meaningful ... the result depends on
the update form. So coding something like (if (setf x (...)) ... )
might make demons fly out of your nose. Lisp doesn't really have
separately compilable modules, but that could be worse if, with latent
types, the compiler had no way of determining if a return value was
present [think returning (values)].
[I'm also firmly in the manifest typing camp - not for runtime
efficiency (though that's a perk) - but simply because I think the
compiler should help me as much as possible by catching obvious
mistakes and I am not yet convinced that type inferencing is fully
sound. I prefer the term "manifest" to "static" because I know some
typing has to done latently at runtime, but I prefer to see the
possibilities directly expressed in the source. I'm content that
others have different preferences so long as we're not going blind
looking for the reason "compatible" code won't integrate.]

Scheme took a step in the right direction by introducing a boolean
type distinct from null (nil), but then blew it by (sort of) copying
Lisp's generalized booleans, permitting conditional tests to evaluate
to #F/not-#F, rather than strictly to #F/#T.
[The poem's (CDR (ASSQ KEY A-LIST)) does't work because ASSQ returns
#F if the key is not found. null is not false. Also, in Scheme, null
and '() satisfy LIST?, but do not satisfy PAIR? and so CDR will not
accept them.]

Of course, the situation is much worse in C(++) which conflates
booleans with numbers and where mistyping a certain logical operator
turns the expression from a comparison into an assignment. Worse yet,
any built-in numeric type - as well as pointers and references - may
be used as zero/non-zero boolean. Python copied the zero/nonzero
nonsense and then further confused the issue by creating unique
"false" values for every other built-in type and even letting the
programmer override the handling of "true" values.


I've arrived at the point where I think the syntax of a conditional
form [in whatever language] should force an explicitly coded test that
evaluates to true/false. Let the compiler worry about optimization.

YMMV.

>Cause mostly, it just makes me think of this wonderful poem from a
>couple of decades ago (appended below).

Cute!

> -- Don
George

TheFlyingDutchman

unread,
Mar 9, 2011, 8:22:50 AM3/9/11
to
If this a valid Common Lisp "boolean if"?

(defmacro bif ( condition then &optional else)
`(let ( (result ,condition ) )
(if (not (or (eq result t) (eq result nil)))
(error "error using bif: condition result is not boolean"))
(cond
(result ,then)
(t ,else))))

Teemu Likonen

unread,
Mar 9, 2011, 9:17:41 AM3/9/11
to

Your macro secretly binds variable RESULT which user certainly doesn't
expect.

(let ((result "my value"))
(bif t result "else"))
=> T ; But "my value" expected.

Create an uninterned symbol for your macro so it can't be seen
elsewhere. You can do that with GENSYM (or MAKE-SYMBOL). Here's a
version which fixes the binding problem and signals a correctable error
with ASSERT.

(defmacro bif (test then &optional else)
(let ((result (gensym)))
`(let ((,result ,test))
(assert (or (not ,result)
(eql ,result t))
(,result)
"Test didn't evaluate to T or NIL: ~S." ,result)
(if ,result ,then ,else))))

Tim Bradshaw

unread,
Mar 9, 2011, 10:53:36 AM3/9/11
to
On 2011-03-09 13:22:50 +0000, TheFlyingDutchman said:

> If this a valid Common Lisp "boolean if"?

No, or at least it is not good style, because it binds RESULT.

Something like this avoids that and is also perhaps clearer:

(defmacro absurdly-fussy-if (c true &optional false)
(let ((cn (make-symbol "C")))
`(let ((,cn ,c))
(etypecase ,cn
(boolean
(if ,cn ,true ,false))))))

WJ

unread,
Mar 9, 2011, 11:17:10 AM3/9/11
to
Pascal J. Bourguignon wrote:

> George Neuner <gneu...@comcast.net> writes:
>
> > On Mon, 28 Feb 2011 20:00:03 -0800 (PST), TheFlyingDutchman
> > <zzbb...@aol.com> wrote:
> >
> >>> [CL] MEMBER is both a predicate and a searching function.
> > > >
> > > Am I wrong in saying that in Scheme documentation they would not refer
> > > to a function like "member" as a predicate?
> >
> > I think that's probably correct ... Scheme predicates return #T or #F,
> > but member returns a list if the object is found. It is potentially
> > confusing, though, because member returns #F if the object is not
> > found.
>
> Yes, at least, Common Lisp is consistent here, and returns () when the
> element is not found... But in scheme, () is true.

So? Scheme is consistent.

guile> (member 'd '(a b #f c))
#f
guile> (member #f '(a b #f c))
(#f c)
guile> (if (member #f '(a b #f c)) 'yes 'no)
yes
guile> (if (member 'd '(a b #f c)) 'yes 'no)
no

Not confusing. If member succeeds, the value returned is non-false.
If it fails, the returned value is false.
Anything other than #f can be used instead of #t by a predicate
to indicate success.

Pascal J. Bourguignon

unread,
Mar 9, 2011, 11:26:58 AM3/9/11
to
Tim Bradshaw <t...@tfeb.org> writes:

(defmacro absurdly-fussy-if-but-probably-more-efficient (c true &optional false)
(let ((cn (gensym)))
`(let ((,cn ,c))
(case ,cn
((t) ,true)
((nil) ,false)
(otherwise (error "~S expected a boolean, instead of the generalized boolean ~S"
'absurdly-fussy-if-but-probably-more-efficient ,cn))))))

It also issues a better error message.

CL-USER> (disassemble (compile nil (lambda () (ABSURDLY-FUSSY-IF (get-some-bool) (print 'true) (print 'false)))))
WARNING: Function GET-SOME-BOOL is not defined

Disassembly of function NIL
(CONST 0) = GET-SOME-BOOL
(CONST 1) = TRUE
(CONST 2) = FALSE
(CONST 3) = #:C
(CONST 4) = (BOOLEAN)
(CONST 5) = SYSTEM::TYPECASE-ERROR-STRING
(CONST 6) = (OR BOOLEAN)
(CONST 7) = SYSTEM::ETYPECASE-FAILED
0 required arguments
0 optional arguments
No rest parameter
No keyword parameters
25 byte-code instructions:
0 (CALL0 0) ; GET-SOME-BOOL
2 (PUSH)
3 (LOAD&JMPIFNOT 0 L10)
6 (LOAD&PUSH 0)
7 (T)
8 (JMPIFNOTEQ L24)
10 L10
10 (LOAD&JMPIF 0 L21)
13 (CONST 2) ; FALSE
14 L14
14 (PUSH)
15 (PUSH-UNBOUND 1)
17 (CALLS1 142) ; PRINT
19 (SKIP&RET 2)
21 L21
21 (CONST 1) ; TRUE
22 (JMP L14)
24 L24
24 (LOAD&PUSH 0)
25 (CONST&PUSH 3) ; #:C
26 (CONST&PUSH 4) ; (BOOLEAN)
27 (CALL2&PUSH 5) ; SYSTEM::TYPECASE-ERROR-STRING
29 (CONST&PUSH 6) ; (OR BOOLEAN)
30 (CALL 3 7) ; SYSTEM::ETYPECASE-FAILED
33 (SKIP&RET 2)
NIL

CL-USER> (disassemble (compile nil (lambda () (ABSURDLY-FUSSY-IF-BUT-PROBABLY-MORE-EFFICIENT (get-some-bool) (print 'true) (print 'false)))))
WARNING: Function GET-SOME-BOOL is not defined

Disassembly of function NIL
(CONST 0) = GET-SOME-BOOL
(CONST 1) = TRUE
(CONST 2) = FALSE
(CONST 3) = "~S expected a boolean, instead of the generalized boolean ~S"
(CONST 4) = ABSURDLY-FUSSY-IF-BUT-PROBABLY-MORE-EFFICIENT
0 required arguments
0 optional arguments
No rest parameter
No keyword parameters
20 byte-code instructions:
0 (CALL0 0) ; GET-SOME-BOOL
2 (PUSH)
3 (LOAD&PUSH 0)
4 (T)
5 (JMPIFEQ L18)
7 (LOAD&JMPIF 0 L21)
10 (CONST 2) ; FALSE
11 L11
11 (PUSH)
12 (PUSH-UNBOUND 1)
14 (CALLS1 142) ; PRINT
16 (SKIP&RET 2)
18 L18
18 (CONST 1) ; TRUE
19 (JMP L11)
21 L21
21 (CONST&PUSH 3) ; "~S expected a boolean, instead of the generalized boolean ~S"
22 (CONST&PUSH 4) ; ABSURDLY-FUSSY-IF-BUT-PROBABLY-MORE-EFFICIENT
23 (LOAD&PUSH 2)
24 (CALLSR 2 30) ; ERROR
NIL

WJ

unread,
Mar 9, 2011, 11:44:38 AM3/9/11
to
Raymond Wiker wrote:

"8 lines" of code? COBOL is insanely prolix. I guess that's why
the pompous and pretentious love it so.


Using Scheme:

guile> (define (hexc->num c) (string-index "0123456789abcdef" c))
guile> (hexc->num #\c)
12
guile> (hexc->num #\0)
0
guile> (hexc->num #\9)
9
guile> (hexc->num #\f)
15

Of course, you would actually use this:

guile> (string->number "f")
#f
guile> (string->number "f" 16)
15

WJ

unread,
Mar 9, 2011, 11:47:39 AM3/9/11
to
Edmunds Cers wrote:

> TheFlyingDutchman <zzbb...@aol.com> writes:
>
> > But as you point out, since every value returned falls into the set of
> > values of a "generalized boolean" (nil or not-nil) it could be argued
> > that every Common Lisp function is a predicate.
>
> This stems directly from CL treating NIL as false and any other value as
> true. Since a predicate is just a function that returns a truth value,

> every CL function can be seen as a predicate. But I don't see anything


> wrong with that. (C does the same, only there 0 is the false value.)
>

MatzLisp (Ruby) is similar. The only two things that are not true
are nil and false.

Tim Bradshaw

unread,
Mar 9, 2011, 11:56:29 AM3/9/11
to
On 2011-03-09 16:26:58 +0000, Pascal J. Bourguignon said:

> (defmacro absurdly-fussy-if-but-probably-more-efficient (c true
> &optional false)
> (let ((cn (gensym)))
> `(let ((,cn ,c))
> (case ,cn
> ((t) ,true)
> ((nil) ,false)
> (otherwise (error "~S expected a boolean, instead of the
> generalized boolean ~S"
> 'absurdly-fussy-if-but-probably-more-efficient ,cn))))))

My theory was that my version would signal some kind of type-error
which is what this really is (obviously yours can be changed to do this
trivially), and also typecase might be something a type-inferencing
compiler can do useful things with, but then it can probably do things
with case as well. Yours is clearer, for sure.

WJ

unread,
Mar 9, 2011, 12:00:07 PM3/9/11
to
George Neuner wrote:

> Also, in Scheme, null
> and '() satisfy LIST?, but do not satisfy PAIR? and so CDR will not
> accept them.]

null is unknown to Guile Scheme and Bigloo Scheme.

Tim Bradshaw

unread,
Mar 9, 2011, 12:20:26 PM3/9/11
to
On 2011-03-09 16:44:38 +0000, WJ said:

> Using Scheme:
>
> guile> (define (hexc->num c) (string-index "0123456789abcdef" c))

You probably actually mean this

(define (hexc->integer c)
(string-index "0123456789abcdef"
(lambda (cc)
(char-ci=? c cc))))

or in CL:

(defun hexc->integer (c)
(position c "0123456789abcdef"
:test #'char-equal))

Which is only a little clearer.

WJ

unread,
Mar 9, 2011, 12:35:41 PM3/9/11
to
Don Geddis wrote:

> George Neuner <gneu...@comcast.net> wrote on Tue, 01 Mar 2011:
> > I really like the notion that boolean is a distinct type with distinct
> > true and false values
>
> OK, that sounds reasonable at first glance...
>
> > ... it occasionally forces an explicit test to the compiler
>
> Yeah, that. (See below.)
>
> > but it avoids a whole host of semantic problems in the most common
> > case of dealing with binary YES/NO logic.
>
> Can you provide some more details about what "semantic problems" you
> have in mind?
>
> Cause mostly, it just makes me think of this wonderful poem from a
> couple of decades ago (appended below).
>
> -- Don
> ____________________________________________________________________________

> ___ Don Geddis http://don.geddis.org/
> d...@geddis.org
>
> ----------------------------------------------------------------------------
> --- A Short Ballad Dedicated to the Growth of Programs

Using Guile Scheme:

(define a-list '((foo . 22) (bar . 44) (baz . #f)))

(define (look key alist)
(cond ((assoc key alist) => cdr)
(else #f)))

guile> (look 'bar a-list)
44
guile> (look 'baz a-list)
#f
guile> (look 'bazzy a-list)
#f

If the value found is #f, you won't know whether it was found
or not.


For a one-liner, just make sure that cdr always has something
valid upon which to work:

guile> (cdr (or (assoc 'gloomy a-list) '(0 . #f)))
#f
guile> (cdr (or (assoc 'foo a-list) '(0 . #f)))
22
guile> (cdr (or (assoc 'baz a-list) '(0 . #f)))
#f
guile> (cdr (or (assoc 'bar a-list) '(0 . #f)))
44

WJ

unread,
Mar 9, 2011, 9:11:02 PM3/9/11
to
WJ wrote:

>
> For a one-liner, just make sure that cdr always has something
> valid upon which to work:
>
> guile> (cdr (or (assoc 'gloomy a-list) '(0 . #f)))
> #f
> guile> (cdr (or (assoc 'foo a-list) '(0 . #f)))
> 22
> guile> (cdr (or (assoc 'baz a-list) '(0 . #f)))
> #f
> guile> (cdr (or (assoc 'bar a-list) '(0 . #f)))
> 44

guile> (cdr (or (assoc 'gloomy a-list) '(_ . #f)))
#f

TheFlyingDutchman

unread,
Mar 9, 2011, 9:32:42 PM3/9/11
to
Is there a way for a macro to handle the &optional parameter
differently depending on if it is present or not? So that in the case
of bif, it would generate only an "if then" if the else is not
supplied, or generate "if then else" when else is supplied?

ie.

(bif (= 2 a) (dostuff))

would not generate code that has a bare "nil" sitting as one branch of
an if/case/cond?

Joshua Taylor

unread,
Mar 9, 2011, 10:25:38 PM3/9/11
to

How about

(defmacro minimal-code-if (test-form
then-form
&optional
(else-form nil else-form-p))
(if else-form-p
`(if ,test-form ,then-form ,else-form)
`(if ,test-form ,then-form)))

or

(defmacro minimal-code-if (test-form
then-form
&optional
(else-form nil else-form-p))
`(if ,test-form ,then-form
,@(when else-form-p
(list else-form))))

CL-USER> (macroexpand '(minimal-code-if x a b))
(IF X A B)
T

CL-USER> (macroexpand '(minimal-code-if x a))
(IF X A)
T

?

TheFlyingDutchman

unread,
Mar 10, 2011, 4:24:16 AM3/10/11
to
>
> How about
>
> (defmacro minimal-code-if (test-form
>                            then-form
>                            &optional
>                            (else-form nil else-form-p))
>   (if else-form-p
>     `(if ,test-form ,then-form ,else-form)
>     `(if ,test-form ,then-form)))
>

That does the trick! Thanks.

Antony

unread,
Mar 10, 2011, 2:41:52 AM3/10/11
to
On 3/8/2011 1:35 PM, George Neuner wrote:
>...lots of stuff snipped

> Clearly it is shorter to type "(if obj ..." - or whatever conditional
> form floats your boat - then the equivalent "(if (not (eq obj nil))
> ...". The shorthand is fine if that is exactly what you meant for the
>...lots of stuff snipped
Why is
(if obj
not good and

(if (not (eq obj nil))
better (assuming that's what you are implying)

I am thinking they mean the exact same thing.
What does the compiler do with the first that is different from what it
does with the second?
-Antony

Pascal J. Bourguignon

unread,
Mar 10, 2011, 10:56:11 AM3/10/11
to
Antony <remove+spam...@gmail.com> writes:

Nothing.


CL-USER> (disassemble (compile nil (lambda (x y z) (if x y z))))

Disassembly of function NIL
3 required arguments


0 optional arguments
No rest parameter
No keyword parameters

6 byte-code instructions:
0 (LOAD&JMPIF 3 L6)
3 (LOAD 1)
4 (SKIP&RET 4)
6 L6
6 (LOAD 2)
7 (SKIP&RET 4)
NIL
CL-USER> (disassemble (compile nil (lambda (x y z) (if (not (null x)) y z))))

Disassembly of function NIL
3 required arguments


0 optional arguments
No rest parameter
No keyword parameters

6 byte-code instructions:
0 (LOAD&JMPIF 3 L6)
3 (LOAD 1)
4 (SKIP&RET 4)
6 L6
6 (LOAD 2)
7 (SKIP&RET 4)
NIL
CL-USER> (disassemble (compile nil (lambda (x y z) (if (not (eq x nil)) y z))))

Disassembly of function NIL
3 required arguments


0 optional arguments
No rest parameter
No keyword parameters

6 byte-code instructions:
0 (LOAD&JMPIF 3 L6)
3 (LOAD 1)
4 (SKIP&RET 4)
6 L6
6 (LOAD 2)
7 (SKIP&RET 4)
NIL
CL-USER>

Marco Antoniotti

unread,
Mar 10, 2011, 11:02:12 AM3/10/11
to
On Mar 10, 4:56 pm, "Pascal J. Bourguignon" <p...@informatimago.com>
wrote:

Question: is this your CL compiler assembly output?

Cheers
--
MA

Pascal J. Bourguignon

unread,
Mar 10, 2011, 11:41:44 AM3/10/11
to
Marco Antoniotti <mar...@gmail.com> writes:

> On Mar 10, 4:56 pm, "Pascal J. Bourguignon" <p...@informatimago.com>
> wrote:
>> CL-USER> (disassemble (compile nil (lambda (x y z) (if (not (eq x nil)) y z))))
>>
>> Disassembly of function NIL
>> 3 required arguments
>> 0 optional arguments
>> No rest parameter
>> No keyword parameters
>> 6 byte-code instructions:
>> 0     (LOAD&JMPIF 3 L6)
>> 3     (LOAD 1)
>> 4     (SKIP&RET 4)
>> 6     L6
>> 6     (LOAD 2)
>> 7     (SKIP&RET 4)
>> NIL
>

> Question: is this your CL compiler assembly output?

Yes. One advantage of clisp is that it has a (virtual) lisp machine
with a nice assembler code.

TheFlyingDutchman

unread,
Mar 10, 2011, 9:51:41 PM3/10/11
to
> Here's a
> version which fixes the binding problem and signals a correctable error
> with ASSERT.
>
Thanks for mentioning assert. I just saw how it allows you to modify a
value and continue if the code is being run from the REPL.
0 new messages