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

Common Lisp/Scheme Comparison

433 views
Skip to first unread message

Ray Dillinger

unread,
Oct 14, 2003, 4:01:05 PM10/14/03
to

A little while ago, because of a thread on CLL, I decided to compare.
I'm actually pretty neutral about the lispy language choice at this
point. I don't want to argue about anything; I just think this is
an interesting list.

I really like the aesthetics of scheme -- how all values are just
values, regardless of whether they are functions or escape procedures
or anything else. I like the generality of its semantic constructs
and the way things are made as simple as they can reasonably be.

On the other hand, there are lots of points in the list below where
Common Lisp has a clear advantage, and most of them are at their most
acute in large shops or on large projects. I've tried to be neutral
here. Scheme uses a lot more characters below, but that's usually
because the situation w/r/t scheme is more complicated, or, in a few
cases, because needs as basic as breathing for the writing of portable
code have been left unspecified.

The first version of this list was posted on CLL. A few corrections
and additions have made their way into this version based on feedback
recieved there. Although this is of interest to both groups, I
consider it "risky" to crosspost any language comparisons between
the two groups. So I'm posting it again, now, for CLS.

Additions and corrections are, of course, still welcome.

Bear

PS. Hope you're using a monospace font!


What Common Lisp has got: What Scheme has got:

Much better developed standard SLIB + SRFI's + a hundred little
libraries libs that each do things differently
and aren't very standardized.

(Arguably Scheme is the place where new ideas fight for mindshare
and prove themselves - but the fights and the multiplicity
of contenders commits most code to one idea or another and
limits the code's interoperability, longevity, and/or
portability.

Some of CL being a "bigger" language though isn't libraries
or anything that would even be useful in scheme; it's additional
functions that are necessary because of other language choices,
like function values and data values being separate and capable
of coexisting in the same variable name/symbol.)


A well-defined comprehensive A well-defined minimal spec plus
spec and several implementations dozens of variously comprehensive
which provide some extensions. implementations.

one-shot continuations only. Multi-use first-class continuations.
Scheme just wins on this point.

(I have heard the arguments about whether multi-use
continuations are worth the cost of stack copying, or the
cost of heap-allocating and garbage collecting invocation
frames. I don't care. I'm just noting here that you can
do a *LOT* of things with them that are hard to do without
them.)

Lots of iterative constructs Memory-safe tail recursion avoids
the need for iteration syntax.
There's a looping construct, but
it's more complicated than tail
recursion so hardly anyone uses it.
If you care for iterators, you can
roll your own by using macros to
wrap it.

Both Lexically and Dynamically Lexical scope only, per the standard.
scoped special vars. Common Dynamically scoped vars are provided
Lisp just wins on this point. by some implementations as an extension
but code using them is not portable.
They can also be simulated using
unwind-protect and before/after
thunks, or if the implementation
provides mutable first-class
environments.

(I have heard the arguments about whether Dynamic Scoping is
or is not a Bad Idea. I don't care. I wouldn't like it as
the default binding mechanism, but there are things you can
do easier with dynamically bound variables available than
without.)


C numeric types plus bignums, The standard defines the same numeric
rationals, and complex nums, types, except unsigned int types, but
but no exact/inexact distinction. implementors aren't required to provide
all. Rationals are common, complex nums
less so. An exact/inexact distinction
is required by the standard but
properly implemented in only about
3/4 of scheme systems. In a good
implementation, numerics (capabilities
and correctness) are better than most
CLs; on average, they are worse.

Optional type declarations Optional type declarations provided
allow blazing fast numeric by a few implementations as extensions.
code to skip typechecking. Code using them is nonportable. Some
Common Lisp just wins on numeric implementations provide blazing speed
calculation speed. but generally at the expense of numeric
type richness and/or standard
conformance.

Typed and untyped vectors. Untyped vectors only. Uniform numeric
vectors are a SRFI but it is not possible
to both provide the syntax required by
that SRFI and be standard-compliant.
Typed vectors other than numeric don't
appear in any scheme systems I know.

Signals and conditions, catch Roll your own using continuations,
and throw. or use any of several libraries.

CLOS Roll your own objects using closures
and macros, or any of several OO
libraries. TinyCLOS and Meroon are
the most popular.

Well-defined standard module At least three competing well-defined
system. Common Lisp just wins module systems which it's a pain in
on this point. the butt to move modules between.
(or roll your own using scope, macros,
and/or preprocessing code).

Readtables for low-level Implementation-defined means of doing
macrology. Common Lisp wins low-level macrology - none of it is
here. portable, and low-level macros aren't
provided by all implementations.

gensym tricks to avoid implicit hygienic macros with define-syntax and
variable captures in high-level syntax-case. You *can't* capture a
macros. variable in a macro except explicitly.

(Different people claim this as a "win" for both languages.
I don't care. There is little difference in what I can do
with it, nor in how hard it is to do it, and most folks have
very different ideas than me about what macros are good for,
so I'm not the guy to judge a winner here.)

One-argument eval assumes environment specifier is second arg to
environment eval, allowing access to multiple
environments. Scheme just wins here.
Note that mutable environments, which
are compatible with but not required
by the standard, triple the value of
this feature.

Lambda syntax supports keyword Available as add-on library developed
arguments & default vals for using macros, but widely ignored.
optional arguments.

Symbols have properties, Variables have values and also names.
including but not limited to The names are lexically indistinguishable
function value and data value. from symbols but the value of a variable
is not a property of its name symbol.
Property lists are an extension
provided by relatively few schemes.

Native hash tables. Hash tables are an extension provided by
some implementations; otherwise there are
good libraries for hash tables.

Well-defined means of doing A fragile hack that depends on common
binary I/O. Common Lisp just character encodings and/or assumption
wins here. that character ports act as byte ports.
Binary I/O provided as extension by
most implementations, but agreement
about the names, semantics, and
argument signatures of the functions
that do it isn't complete enough for
portable code to be written.

Assertions. Common Lisp just In scheme you have to do this as two
wins here. macros; one for development, that signals
an error if the condition isn't true, and
one for production code which "expands"
into nothing and gets out of the way.
The compiler will not use your assertions
to produce better code.


Large runtime environment Small runtime environment, easily
embeddable. Scheme wins here.

Erik Max Francis

unread,
Oct 14, 2003, 5:55:32 PM10/14/03
to
Ray Dillinger wrote:

> A little while ago, because of a thread on CLL, I decided to compare.
> I'm actually pretty neutral about the lispy language choice at this
> point. I don't want to argue about anything; I just think this is
> an interesting list.

As a general summary of the differences, I found this document to be helpful:

http://arti.vub.ac.be/www/cursus/2002-2003/algo2/Differences%20between%20Scheme%20and%20Common%20Lisp.htm

--
Erik Max Francis && m...@alcyone.com && http://www.alcyone.com/max/
__ San Jose, CA, USA && 37 20 N 121 53 W && &tSftDotIotE
/ \ I'll tell them that their daddy was / A good man
\__/ India Arie

Eli Barzilay

unread,
Oct 14, 2003, 7:00:30 PM10/14/03
to
Ray Dillinger <be...@sonic.net> writes:
> [...]

Something is missing... Oh yes, you can't expect people to be serious
about this without *at least* 200 followups.

--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://www.barzilay.org/ Maze is Life!

prunes...@comcast.net

unread,
Oct 14, 2003, 9:16:31 PM10/14/03
to
Eli Barzilay <e...@barzilay.org> writes:

> Ray Dillinger <be...@sonic.net> writes:
>> [...]
>
> Something is missing... Oh yes, you can't expect people to be serious
> about this without *at least* 200 followups.

Is too.
Is not.
...mumble...obviously...brain-dead...Naggum...tail-recursion...
...infix...do my homework...hygiene...Nazis...

Pascal Costanza

unread,
Oct 15, 2003, 4:21:04 PM10/15/03
to
Ray Dillinger wrote:
> A little while ago, because of a thread on CLL, I decided to compare.
> I'm actually pretty neutral about the lispy language choice at this
> point. I don't want to argue about anything; I just think this is
> an interesting list.
>
> I really like the aesthetics of scheme -- how all values are just
> values, regardless of whether they are functions or escape procedures
> or anything else. I like the generality of its semantic constructs
> and the way things are made as simple as they can reasonably be.
>
> On the other hand, there are lots of points in the list below where
> Common Lisp has a clear advantage, and most of them are at their most
> acute in large shops or on large projects. I've tried to be neutral
> here. Scheme uses a lot more characters below, but that's usually
> because the situation w/r/t scheme is more complicated, or, in a few
> cases, because needs as basic as breathing for the writing of portable
> code have been left unspecified.
>
> The first version of this list was posted on CLL. A few corrections
> and additions have made their way into this version based on feedback
> recieved there. Although this is of interest to both groups, I
> consider it "risky" to crosspost any language comparisons between
> the two groups. So I'm posting it again, now, for CLS.
>
> Additions and corrections are, of course, still welcome.

Although I consider myself to be more on the Common Lisp side, I take
the freedom to reply in this newsgroup. ;) (I also think that I am able
to distinguish between personal preference and "objective" facts.)

Your list is great, and I especially appreciate its neutralness.

I think two items are missing.

+ Proper tail recursion (or better: space-safe tail recursion): Scheme
requires this, Common Lisp doesn't. It can be shown that space-safe tail
recursion objectively allows more programs to be run. Scheme wins here.

One possible downside is that tail call elimination makes debugging
harder in certain scenarios. I guess it should be possible in a Scheme
development environment to switch off proper tail recursion when
debugging programs. However, I don't know whether there are Scheme
implementations that actually do that. In general, I think, proper tail
recursion as the default is the better choice.

+ Booleans are a distinct data type in Scheme whereas Common Lisp has a
notion of generalized booleans. The latter means that NIL is considered
to be false and all other values are considered to be true - there is no
distinct boolean type. (The symbol t is used as the default value to
indicate truth when no other value is used to provide more information.)

Schemers seem to claim that their choice makes programs clearer and less
prone to errors. (I don't know actually - I think Schemers can better
explain why they think their choice is the better one.)

Common Lispers appreciate the fact that generalized booleans makes code
more compact in most cases. When there is the need to explicitly
distinguish between NIL and false, one can create a unique symbol for
that purpose. The need to do this occurs rarely.

(RnRS doesn't define MAKE-SYMBOL or GENSYM, and this is probably why
Scheme really needs to define a distinct boolean type.)

All in all, I think that there is no clear "winner" in this case - both
solutions have their specific trade-offs.


(Ray, feel free to include this text into your list, changed or unchanged.)


Pascal

Joe Marshall

unread,
Oct 16, 2003, 11:01:33 AM10/16/03
to
Pascal Costanza <cost...@web.de> writes:

> One possible downside is that tail call elimination makes debugging
> harder in certain scenarios. I guess it should be possible in a Scheme
> development environment to switch off proper tail recursion when
> debugging programs. However, I don't know whether there are Scheme
> implementations that actually do that. In general, I think, proper
> tail recursion as the default is the better choice.

One should note, however, that there is no less information present
in a tail-recursive program than there is in any loop.

In MIT Scheme, there is an object called the `history' which is a
bounded structure that keeps track of the n-most-recent evaluations (n
is settable by the user, and when n is exceeded, the older information
is discarded). This allows you to keep tail recursion, but it adds
a nice amount of debugging info.

> + Booleans are a distinct data type in Scheme whereas Common Lisp has
> a notion of generalized booleans. The latter means that NIL is
> considered to be false and all other values are considered to be true
> - there is no distinct boolean type. (The symbol t is used as the
> default value to indicate truth when no other value is used to provide
> more information.)
>
> Schemers seem to claim that their choice makes programs clearer and
> less prone to errors. (I don't know actually - I think Schemers can
> better explain why they think their choice is the better one.)

I don't know if this is universally accepted. Personally, I find it
more cumbersome to have the empty list count as #t. It may be `more
correct' but it seems `less pragmatic'.

PLT Scheme takes it even further by introducing a `void' object
that is neither the empty list or false, and counts as a true object
for the purposes of conditionals. You can imagine a whole plethora
of `magic tokens' that are unique representations of some sort of
`non object': the result of a one-armed IF when the branch isn't
taken, the default value of a COND clause when no conditions match,
the return value of a definition.

But you can make an isomorphism between all these objects and symbols,
so why not just use symbols?

> Common Lispers appreciate the fact that generalized booleans makes
> code more compact in most cases. When there is the need to explicitly
> distinguish between NIL and false, one can create a unique symbol for
> that purpose. The need to do this occurs rarely.
>
> (RnRS doesn't define MAKE-SYMBOL or GENSYM, and this is probably why
> Scheme really needs to define a distinct boolean type.)

Prior to RnRS specifying '() != #f, the traditional thing to do
was use continuation-passing-style when you needed to distinguish
a `null result' from `a result that happened to be null'.

Pascal Costanza

unread,
Oct 16, 2003, 11:24:58 AM10/16/03
to
Joe Marshall wrote:

> In MIT Scheme, there is an object called the `history' which is a
> bounded structure that keeps track of the n-most-recent evaluations (n
> is settable by the user, and when n is exceeded, the older information
> is discarded). This allows you to keep tail recursion, but it adds
> a nice amount of debugging info.

This sounds like a very good idea. This trick should also work for
loops, shouldn't it?

> Prior to RnRS specifying '() != #f, the traditional thing to do
> was use continuation-passing-style when you needed to distinguish
> a `null result' from `a result that happened to be null'.

That's a very terse description. :) How does this work? (a link to some
literature would be ok)


Pascal

Ray Dillinger

unread,
Oct 16, 2003, 11:22:39 AM10/16/03
to
Joe Marshall wrote:

>
> Pascal Costanza <cost...@web.de> writes:
> > + Booleans are a distinct data type in Scheme whereas Common Lisp has
> > a notion of generalized booleans. The latter means that NIL is
> > considered to be false and all other values are considered to be true
> > - there is no distinct boolean type. (The symbol t is used as the
> > default value to indicate truth when no other value is used to provide
> > more information.)

> > Schemers seem to claim that their choice makes programs clearer and
> > less prone to errors. (I don't know actually - I think Schemers can
> > better explain why they think their choice is the better one.)

> I don't know if this is universally accepted. Personally, I find it
> more cumbersome to have the empty list count as #t. It may be `more
> correct' but it seems `less pragmatic'.

I rarely define syntax, but some of the syntax I do often define
is 'nif' and 'ncond' - which are basically if and cond except that
for purposes of nif and ncond the empty list is indistinguishable
from false. These forms frequently collapse recursion base cases
at the tail of a list, and are often handy otherwise.

Bear

Ray Dillinger

unread,
Oct 16, 2003, 11:35:33 AM10/16/03
to
Erik Max Francis wrote:
>
> Ray Dillinger wrote:
>
> > A little while ago, because of a thread on CLL, I decided to compare.
> > I'm actually pretty neutral about the lispy language choice at this
> > point. I don't want to argue about anything; I just think this is
> > an interesting list.
>
> As a general summary of the differences, I found this document to be helpful:
>
> http://arti.vub.ac.be/www/cursus/2002-2003/algo2/Differences%20between%20Scheme%20and%20Common%20Lisp.htm
>

The document could use updating now that eval has been defined in R5RS.
There really *is* an effective code/data isomorphism in scheme now, and
it really *is* required by thes standard.

Bear

Joe Marshall

unread,
Oct 16, 2003, 2:26:34 PM10/16/03
to
Pascal Costanza <cost...@web.de> writes:

> Joe Marshall wrote:
>
>> In MIT Scheme, there is an object called the `history' which is a
>> bounded structure that keeps track of the n-most-recent evaluations (n
>> is settable by the user, and when n is exceeded, the older information
>> is discarded). This allows you to keep tail recursion, but it adds
>> a nice amount of debugging info.
>
> This sounds like a very good idea. This trick should also work for
> loops, shouldn't it?

Yep.

>> Prior to RnRS specifying '() != #f, the traditional thing to do
>> was use continuation-passing-style when you needed to distinguish
>> a `null result' from `a result that happened to be null'.
>
> That's a very terse description. :) How does this work? (a link to
> some literature would be ok)

(define (lookup element list success failure)
(cond ((null? list) (failure))
((eq? (car list) element) (success (car list)))
(else (lookup element (cdr list) success failure))))

Even if you use null as a key, you can distinguish between
finding it in the list vs. running off the end.

If you arrange for your compiler to inline the call to lookup,
the resulting code is no less efficient than the obvious
way to do it.

Pascal Costanza

unread,
Oct 16, 2003, 2:33:44 PM10/16/03
to
Joe Marshall wrote:

>>>Prior to RnRS specifying '() != #f, the traditional thing to do
>>>was use continuation-passing-style when you needed to distinguish
>>>a `null result' from `a result that happened to be null'.
>>
>>That's a very terse description. :) How does this work? (a link to
>>some literature would be ok)
>
>
> (define (lookup element list success failure)
> (cond ((null? list) (failure))
> ((eq? (car list) element) (success (car list)))
> (else (lookup element (cdr list) success failure))))
>
> Even if you use null as a key, you can distinguish between
> finding it in the list vs. running off the end.
>
> If you arrange for your compiler to inline the call to lookup,
> the resulting code is no less efficient than the obvious
> way to do it.

Great. Thanks!


Pascal

Alex Shinn

unread,
Oct 16, 2003, 10:40:55 PM10/16/03
to
At Wed, 15 Oct 2003 22:21:04 +0200, Pascal Costanza wrote:
>
> + Booleans are a distinct data type in Scheme whereas Common Lisp has a
> notion of generalized booleans. The latter means that NIL is considered
> to be false and all other values are considered to be true - there is no
> distinct boolean type. (The symbol t is used as the default value to
> indicate truth when no other value is used to provide more information.)
>
> Schemers seem to claim that their choice makes programs clearer and less
> prone to errors. (I don't know actually - I think Schemers can better
> explain why they think their choice is the better one.)

Schemers are fairly divided on this, but I personally like the
distinction because I frequently write code of the following form:

(or (search1) (search2) (search3))

(cond ((search4) => proc) (else ...))

where '() is considered a "true" result for any of the searches, and
they would only return #f for not found. If '() is false, then I can't
use searches that return possibly-empty lists.

You can get around this w/ CPS, multiple values and other coding tricks
but it makes the code much more clumsy. Conversely, syntax like the nif
and ncond mentioned in this thread are trivial to define and allow you
to keep both coding styles compact when you have #f and '() separate.

> Common Lispers appreciate the fact that generalized booleans makes code
> more compact in most cases. When there is the need to explicitly
> distinguish between NIL and false, one can create a unique symbol for
> that purpose.

This is a poor hack, since the gensym can't guard against symbols
generated in the future, such as read from a file or over the network.

--
Alex

prunes...@comcast.net

unread,
Oct 16, 2003, 11:40:39 PM10/16/03
to
Alex Shinn <fo...@synthcode.com> writes:

> Schemers are fairly divided on this, but I personally like the
> distinction because I frequently write code of the following form:
>
> (or (search1) (search2) (search3))
>
> (cond ((search4) => proc) (else ...))
>
> where '() is considered a "true" result for any of the searches, and
> they would only return #f for not found. If '() is false, then I can't
> use searches that return possibly-empty lists.

Good point for this case. I like the `=>' syntax so much that
I've installed it in my common lisp system. On the other hand,
CL doesn't distinguish the two cases and the => syntax is
still pretty useful.

> You can get around this w/ CPS, multiple values and other coding tricks
> but it makes the code much more clumsy.

I don't find CPS very clumsy, but that might be me.
Multiple values are clumsy, though. Especially in Scheme (sigh).

>> Common Lispers appreciate the fact that generalized booleans makes code
>> more compact in most cases. When there is the need to explicitly
>> distinguish between NIL and false, one can create a unique symbol for
>> that purpose.
>
> This is a poor hack, since the gensym can't guard against symbols
> generated in the future, such as read from a file or over the network.

The gensym isn't interned, so that shouldn't be an issue.

Alex Shinn

unread,
Oct 17, 2003, 1:33:29 AM10/17/03
to
At Fri, 17 Oct 2003 03:40:39 GMT, prunes...@comcast.net wrote:

>
> Alex Shinn <fo...@synthcode.com> writes:
>
> > This is a poor hack, since the gensym can't guard against symbols
> > generated in the future, such as read from a file or over the network.
>
> The gensym isn't interned, so that shouldn't be an issue.

True, sorry, my mistake.

However, it's still awkward because all of the search functions involved
have to be defined to return that specific gensym, which rules out using
library functions or partial applications thereof. You could define all
library functions to take a failure-symbol argument, but at that point
you might as well make it a failure continuation. Assuming each of the
search procedures take a success and a failure continuation of one
argument, the previous examples become

(search1 identity
(lambda (f)
(search2 identity
(lambda (f)
(search3 identity identity)))))

(search4 <success-thunk> <failure-thunk>)

For common idioms like the (or ...) you could define

(define (cps-or first . rest)
(first identity (if (null? rest)
identity
(lambda (f) (apply cps-or rest)))))

(cps-or search1 search2 search3)

so it's really not awkward if you use a primarily CPS style. The
problem then is that you have to stick to this style and write your
libraries in CPS style - maybe not a bad thing.

--
Alex

Pascal Costanza

unread,
Oct 14, 2003, 5:24:20 PM10/14/03
to
Ray Dillinger wrote:
> A little while ago, because of a thread on CLL, I decided to compare.
> I'm actually pretty neutral about the lispy language choice at this
> point. I don't want to argue about anything; I just think this is
> an interesting list.
>
> I really like the aesthetics of scheme -- how all values are just
> values, regardless of whether they are functions or escape procedures
> or anything else. I like the generality of its semantic constructs
> and the way things are made as simple as they can reasonably be.
>
> On the other hand, there are lots of points in the list below where
> Common Lisp has a clear advantage, and most of them are at their most
> acute in large shops or on large projects. I've tried to be neutral
> here. Scheme uses a lot more characters below, but that's usually
> because the situation w/r/t scheme is more complicated, or, in a few
> cases, because needs as basic as breathing for the writing of portable
> code have been left unspecified.
>
> The first version of this list was posted on CLL. A few corrections
> and additions have made their way into this version based on feedback
> recieved there. Although this is of interest to both groups, I
> consider it "risky" to crosspost any language comparisons between
> the two groups. So I'm posting it again, now, for CLS.
>
> Additions and corrections are, of course, still welcome.

Although I consider myself to be more on the Common Lisp side, I take

the freedom to reply in this newsgroup. ;) (I also think that I am able
to distinguish between personal preference and "objective" facts.)

Your list is great, and I especially appreciate its neutralness.

I think two items are missing.

+ Proper tail recursion (or better: space-safe tail recursion): Scheme
requires this, Common Lisp doesn't. It can be shown that space-safe tail
recursion objectively allows more programs to be run. Scheme wins here.

One possible downside is that tail call elimination makes debugging

harder in certain scenarios. I guess it should be possible in a Scheme
development environment to switch off proper tail recursion when
debugging programs. However, I don't know whether there are Scheme
implementations that actually do that. In general, I think, proper tail
recursion as the default is the better choice.

+ Booleans are a distinct data type in Scheme whereas Common Lisp has a

notion of generalized booleans. The latter means that NIL is considered
to be false and all other values are considered to be true - there is no
distinct boolean type. (The symbol t is used as the default value to
indicate truth when no other value is used to provide more information.)

Schemers seem to claim that their choice makes programs clearer and less
prone to errors. (I don't know actually - I think Schemers can better
explain why they think their choice is the better one.)

Common Lispers appreciate the fact that generalized booleans makes code

more compact in most cases. When there is the need to explicitly
distinguish between NIL and false, one can create a unique symbol for

that purpose. The need to do this occurs rarely.

(RnRS doesn't define MAKE-SYMBOL or GENSYM, and this is probably why
Scheme really needs to define a distinct boolean type.)

All in all, I think that there is no clear "winner" in this case - both

Ray Dillinger

unread,
Oct 17, 2003, 11:55:29 AM10/17/03
to
Pascal Costanza wrote:
>

> + Proper tail recursion (or better: space-safe tail recursion): Scheme
> requires this, Common Lisp doesn't. It can be shown that space-safe tail
> recursion objectively allows more programs to be run. Scheme wins here.

Already mentioned it, across from "Iteration constructs."


> One possible downside is that tail call elimination makes debugging
> harder in certain scenarios. I guess it should be possible in a Scheme
> development environment to switch off proper tail recursion when
> debugging programs. However, I don't know whether there are Scheme
> implementations that actually do that. In general, I think, proper tail
> recursion as the default is the better choice.

I think MITscheme's "history" ringbuffer answers all the debugging
needs I have, even in the presence of continuation-passing and
space-safe tail recursion. As someone else has already mentioned
here, it's really a quite cool thing for scheme implementers to
provide.


> + Booleans are a distinct data type in Scheme whereas Common Lisp has a
> notion of generalized booleans. The latter means that NIL is considered
> to be false and all other values are considered to be true - there is no
> distinct boolean type. (The symbol t is used as the default value to
> indicate truth when no other value is used to provide more information.)

Right. This is actually an important difference.


> (RnRS doesn't define MAKE-SYMBOL or GENSYM, and this is probably why
> Scheme really needs to define a distinct boolean type.)

I don't see the connection here. I thought the Scheme committee decided
those forms were unnecessary due to hygienic macros, and that scheme
needed (not (equal> #f '())) for completely different reasons.


> All in all, I think that there is no clear "winner" in this case - both
> solutions have their specific trade-offs.

That was pretty much my interpretation, too. It's easier for me to
get stuff working with scheme and it's easier to look at code and
understand what it does in scheme -- its semantics are cleaner, its
eval is more flexible, and continuations allow things that, while
they can be done in CL, it's not obvious how to do them.

On the other hand, CL provides a much larger foundation of portable
stuff and standard stuff and the implementation restrictions aren't
as quirky and unpredictable as with scheme. I really don't like the
strange namespace tricks and having to call different functions to
deal with values because they happen to be procedures and so on -
it seems like needless and pointless complication. But dealing with
that complication allows me to put code in a form where I'm confident
of its portability, integratability, performance, etc. Common Lisp
is a pain in the ass to use, and has a lot of cruft that's just plain
ugly, but its standardization, consistency, and the quality of its
few implementations make it worth it.

Bear

David Rush

unread,
Oct 20, 2003, 9:12:32 AM10/20/03
to
On Fri, 17 Oct 2003 03:40:39 GMT, <prunes...@comcast.net> wrote:
> Alex Shinn <fo...@synthcode.com> writes:
(on #f vs NIL)...

>> Schemers are fairly divided on this, but I personally like the
>> distinction because I frequently write code of the following form:
>>
>> (or (search1) (search2) (search3))
>> (cond ((search4) => proc) (else ...))
>>
>> You can get around this w/ CPS, multiple values and other coding tricks
>> but it makes the code much more clumsy.
>
> I don't find CPS very clumsy, but that might be me.

Me too. In fact, I've found it liberating.

> Multiple values are clumsy, though. Especially in Scheme (sigh).

I just use CPS here, too. It's all good really.

david rush
--

Joe Marshall

unread,
Oct 20, 2003, 10:54:05 AM10/20/03
to
David Rush <ku...@gofree.indigo.ie> writes:

Yes, I do too. I meant that the `call-with-values' syntax is
clumsier than just CPSing the values generating form.

Ray Dillinger

unread,
Oct 20, 2003, 12:51:23 PM10/20/03
to
Joe Marshall wrote:

> Yes, I do too. I meant that the `call-with-values' syntax is
> clumsier than just CPSing the values generating form.

I'm increasingly of the opinion that returning multiple values
ought not happen in a language unless variables in that language
can have multiple values.

Bear

David Rush

unread,
Oct 20, 2003, 4:24:49 PM10/20/03
to
On Thu, 16 Oct 2003 11:01:33 -0400, Joe Marshall <j...@ccs.neu.edu> wrote:
> One should note, however, that there is no less information present
> in a tail-recursive program than there is in any loop.

Yes, however, it is worth noting that the elimination of stack frames from
all tail-calls can make it harder to figure out exactly *why* you end
up at an error condition in non-loop cases.

david rush
--
(\x.(x x) \x.(x x)) -> (s i i (s i i))
-- aki helin (on comp.lang.scheme)

0 new messages