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

R6RS vs. XML and Javascript

14 views
Skip to first unread message

Tom Lord

unread,
Oct 3, 2001, 1:33:27 AM10/3/01
to

Leaving aside, for the moment, the question of who would write it,
there ought to be an R6RS.

The RnRS series is often criticized for being too small -- for leaving
out features that are needed for a reasonably complete programming
language. Some notable missing features are an exception mechanism, a
module system, and a non-toy I/O system.

But those criticisms miss the mark. R6RS can do without those
features too.

Instead, R6RS should aim to provide a non-controversial core language,
specifying only the details that all reasonable Scheme implementors
can agree on. To accomplish that, R6RS would have to remove some
features from R5RS: it should unspecify the result of `(eq? '() #f)';
it should unspecify whether or not symbols are case sensative.

What would be the value of such a standard? It would have two quite
valuable applications.

1) It would provide an implementation-independent standard
for exchanging simple code fragments.

2) It would provide a language-independent standard for
the exchange of structured data: the output of `write'
and the input to `read'.

Such a standard could be easily supported by all lisp dialects, and by
many non-lisp languages. The structured data component would be
superior to XML for many applications. The code fragment component
would find use in many kinds of applications and be a superior
replacement for silly little languages like javascripot.

-t

Jeffrey Siegal

unread,
Oct 3, 2001, 2:25:10 AM10/3/01
to
Tom Lord wrote:
> Instead, R6RS should aim to provide a non-controversial core language,
> specifying only the details that all reasonable Scheme implementors
> can agree on.

All? That's never going to happen. Most? I would argue we're there
already. With the possible exception of dynamic-wind, and perhaps
low-level macros, there seems to be little controversy among reasonable
Scheme implementors.

> To accomplish that, R6RS would have to remove some
> features from R5RS: it should unspecify the result of `(eq? '() #f)';

I don't think you are going to get agreement on that, at least not with
the arguments you've made so far. There is a strong reliance argument
for leaving it alone. In particular, R5RS (and IEEE Scheme) has been
the way it is for long enough that code has now been written that way,
and some people are used to it and now prefer it.

> it should unspecify whether or not symbols are case sensative.

Maybe you have a chance on this one, since there seems to be little
value in writing Scheme code that assumes them to be insensitive.

Tom Lord

unread,
Oct 3, 2001, 3:25:13 AM10/3/01
to
In article <3BBAAF46...@quiotix.com>,
Jeffrey Siegal <j...@quiotix.com> wrote:

> * consistency with Scheme `and' and `or'

I don't see any inconsistency there. 'and' and 'or' treat
anything other than #f (including '()) as a true value, just
like booleans in any other context.

There is a consistency issue.

For arguments which are strictly boolean, `and' and `or' have types:

(and <boolean> ...) => <boolean>
(or <boolean> ...) => <boolean>

For homogeneously-typed arguments of any other type, if '() and #f are
the same, we usefully have:

(and <type> ...) => <type>
(or <type> ...) => <type>


But if '() and #f are different, we may have:

(and <type> ...) => <type>
(or <type> ...) => <type>

but those expressions are useless unless #f is included in <type>. So
when defining synthetic types (as opposed to primitive types), we're
encouraged to use `#f' as a member of <type>: in order to make `and'
and `or' useful again. But if we do that, our new synthetic types are
oddly different from the synthetic type built-in to the standard
(`list?').

Another way to say that is that the traditional `nil' value is always
used to represent the default "distinguished case" of any synthetic
type. `and' and `or' are inherited from a tradition that provides
control structures which manage to switch on that distinguished case
while still promising to return the same type as their arguments.

If we split `nil' into '() and #f, then #f is the new natural choice
for the distinguished case of synthetic types. But lists, for some
reason, use '().

> To accomplish that, R6RS would have to remove some
> features from R5RS: it should unspecify the result of `(eq? '() #f)';

I don't think you are going to get agreement on that, at least
not with the arguments you've made so far. There is a strong
reliance argument for leaving it alone. In particular, R5RS
(and IEEE Scheme) has been the way it is for long enough that
code has now been written that way, and some people are used
to it and now prefer it.

The reliance argument makes a good case that instead of requiring '()
and #f to be the same, the standard should permit them to differ. It
doesn't make the case that the standard should continue to require
them to differ.

In addition to consistency, permitting '() and #f to be the same
removes the primary obstacle to cleanly embedding Scheme in Common
Lisp. Generally, it helps interoperability with many dialects of
lisp. Those would be good effects for the standard to have.


-t

Jeffrey Siegal

unread,
Oct 3, 2001, 3:44:07 AM10/3/01
to
Tom Lord wrote:
> The reliance argument makes a good case that instead of requiring '()
> and #f to be the same, the standard should permit them to differ. It
> doesn't make the case that the standard should continue to require
> them to differ.

From the point of view of implementors, it does not, but from the point
of view of users, it does. Users who have code (or coding
habits/preferences) which rely on them being distinct will be harmed if
"standard Scheme" implementations are no longer required to keep them
distict.

Reliance is not overriding reason to not make a change, but it is a
strong argument, and I don't see your arguments as being strong enough
to overcome it. Not even close. But that's just my view.

> In addition to consistency, permitting '() and #f to be the same
> removes the primary obstacle to cleanly embedding Scheme in Common
> Lisp. Generally, it helps interoperability with many dialects of
> lisp. Those would be good effects for the standard to have.

I don't see a lot of interest in embedding Scheme in Common Lisp.
Frankly, I don't see a lot of interest in Common Lisp in the Scheme
world at all. It's largely a distinct community at this point. This is
certainly not proof, but a cursory look at the headers in comp.lang.lisp
did not reveal very many names in common with recent posters on
comp.lang.scheme.

(I haven't read the portion of your reply that talked about and/or,
because it was long and my interest level is limited. If I get a
chance, I'll read and reply to that separately.)

Ji-Yong D. Chung

unread,
Oct 3, 2001, 9:33:45 AM10/3/01
to
Hi
"Tom Lord" <lo...@emf.emf.net> wrote in message
news:trl8p73...@corp.supernews.com...

>
> Leaving aside, for the moment, the question of who would write it,
> there ought to be an R6RS.

I agree.

I just wanted to make a side comment on SRFI
There has been suggestions that SRFI, in
some regards, should/will extend its coverage
and take over the role of RnRS. Therefore,
so the argument goes, R6RS is not needed.

I do not think SRFI can take the place of
RnRS. During the time I have been reading
posts here at comp.lang.scheme (for about a year),
I cannot remember any article which refers to SRFI.
for resolving a technical issue. All articles /posts
refer to R5RS or R4RS.

It seems to me that RnRS stands alone
when it comes to setting the standard
for Scheme or specifying the language.
And without R6RS, I cannot see
how core changes can be made to
Scheme, which really should be evolving.

One question maybe: why can't other document
command as much respect as RnRS and "set Scheme
standard?" I think it comes down to the reputation of
the original authors. In my view, to produce R6RS or
something like it, _and_ to have people feel that the
document is the de facto standard, the document
must be authored by a committee of respectable
Scheme heavyweights.

Or at least, it should have their blessing.


Jeffrey Siegal

unread,
Oct 3, 2001, 10:32:13 AM10/3/01
to
"Ji-Yong D. Chung" wrote:
> One question maybe: why can't other document
> command as much respect as RnRS and "set Scheme
> standard?" I think it comes down to the reputation of
> the original authors.

In part. It is also an issue of different process.

RnRS was a consensus-building process. As such, the output of that
process represents something of real substance. That it represents a
consensus among respected experts adds to that substance, but even if it
were just a large group of knoledgable users rather than experts,

By contrast, the SRFI process is not a consensus-building process.
Anyone can throw up a SRFI and, although there is a discussion period,
there is nothing to prevent that SRFI from eventually being published as
final regardless of what anyone else thinks about it. There are some
very good SRFIs and some truly awful ones (both in the eye of the
beholder, of course). In general, "it's in SRFI xyz" says very little
about technical merit and, even in the case where the author of the SRFI
is well-respected and therefore one would expect the SRFI to have
technical merit, having gone through the SRFI process says nothing about
peer-review or community agreement.

The SRFI process is a good first step for getting ideas written up for
public review and criticism, but there is also a need for a subsequent
step where respected experts take a look and say "SRFIs x, y, and z" are
good ideas and should be part of the language.

Jeffrey Siegal

unread,
Oct 3, 2001, 10:32:44 AM10/3/01
to
"Ji-Yong D. Chung" wrote:
> One question maybe: why can't other document
> command as much respect as RnRS and "set Scheme
> standard?" I think it comes down to the reputation of
> the original authors.

In part. It is also an issue of different process.

RnRS was a consensus-building process. As such, the output of that
process represents something of real substance. That it represents a
consensus among respected experts adds to that substance, but even if it

were just a large group of knowledgeable users rather than experts, a
consensus would still be noteworthy.

By contrast, the SFRI process is not a consensus-building process.

Stephan H.M.J. Houben

unread,
Oct 3, 2001, 10:32:22 AM10/3/01
to
On Wed, 3 Oct 2001 09:33:45 -0400, Ji-Yong D. Chung <virtua...@erols.com>
wrote:

> I just wanted to make a side comment on SRFI
>There has been suggestions that SRFI, in
>some regards, should/will extend its coverage
>and take over the role of RnRS. Therefore,
>so the argument goes, R6RS is not needed.

I think the argument was more that there was absolutely
no agreement on what should be added/changed/removed
from R5RS, so there couldn't be a R6RS.

IMHO, The SRFI's are mostly to prevent people from coming up
with incompatible ways to do essentially the same thing.
As such, they are quite succesful. For example, thanks
to SRFI 9 it is now possible to write quite portable
code with user-defined types.

> I do not think SRFI can take the place of
>RnRS. During the time I have been reading
>posts here at comp.lang.scheme (for about a year),
>I cannot remember any article which refers to SRFI.
>for resolving a technical issue. All articles /posts
>refer to R5RS or R4RS.

Actually, a Google search produced quite a number of
SRFI-related posts, even not counting SRFI announcements.

And then there are quite a lot of people who casually
use let-values or case-lambda in code.

>And without R6RS, I cannot see
>how core changes can be made to
>Scheme, which really should be evolving.

As far as I understand the SRFI process, there
would be no problem to specify an SRFI that dictated
that (eq? #f '()) ==> #t. It is just that most
SRFI's have concentrated on "library" issues.

Frankly, I feel that core Scheme is pretty much OK
and should be left as-is. We need stronger libraries,
sure. Mostly, these libraries are already available
in existing Schemes, but there is much divergence.

This divergence is, IMHO, the problem. It is not useful
to require every Scheme implementation to handle CGI
scripts. It *is* useful to have every Scheme implementation
that *does* handle CGI scripts, to do it in the same way.

>In my view, to produce R6RS or
>something like it, _and_ to have people feel that the
>document is the de facto standard, the document
>must be authored by a committee of respectable
>Scheme heavyweights.

But what if the Scheme heavyweights don't want to?
Anyway, in most standard committees it is usual that
additions have to be "widely implemented" and be
"uncontroversial". So what is your list of
"widely implemented" and "uncontroversial" additions
you would want to make to R5RS? That's no rhetorical
question: I would be really interested in your
opinion on what should be in such a list.

> Or at least, it should have their blessing.

Having the blessing of the implementors is much more
important, since they have to do the coding.

Stephan

Jeffrey Siegal

unread,
Oct 3, 2001, 10:46:29 AM10/3/01
to
Tom Lord wrote:
> So
> when defining synthetic types (as opposed to primitive types), we're
> encouraged to use `#f' as a member of <type>: in order to make `and'
> and `or' useful again.

AND and OR are useful, and most easily understood, as operations on
booleans. Some types (in the category of what you are calling
"synthetic types") contain booleans, and for those types, AND and OR are
useful. Other types, such as numbers, do not contain booleans, and AND
and OR are useless there. But so what? Not every operator is useful on
every type.

I believe it is the tradational idea in Lisp (but not Scheme!) of having
AND and OR "be useful" outside of just booleans (in particular, as an
operator on lists) is ideosyncratic, unclean, and confusing. I, for
one, am glad that Scheme does not share it. NULL? is there, ready and
waiting, when that's what you mean.

felix

unread,
Oct 3, 2001, 11:51:08 AM10/3/01
to
The SRFI process appears to be out of steam, IMHO.

The two draft SRFI's are way beyond their draft periods,
SRFI-22 is final, but has never been announced and the
list of conforming implementations is rather out of date.


cheers,
felix


Ji-Yong D. Chung

unread,
Oct 3, 2001, 12:09:29 PM10/3/01
to
Hi
"Stephan H.M.J. Houben" <ste...@pcrm.win.tue.nl> wrote in message
news:slrn9rm8ce....@pcrm.win.tue.nl...

> On Wed, 3 Oct 2001 09:33:45 -0400, Ji-Yong D. Chung
<virtua...@erols.com>
> wrote:
> > I just wanted to make a side comment on SRFI
> >There has been suggestions that SRFI, in
> >some regards, should/will extend its coverage
> >and take over the role of RnRS. Therefore,
> >so the argument goes, R6RS is not needed.
>
> I think the argument was more that there was absolutely
> no agreement on what should be added/changed/removed
> from R5RS, so there couldn't be a R6RS.
>
> IMHO, The SRFI's are mostly to prevent people from coming up
> with incompatible ways to do essentially the same thing.
> As such, they are quite succesful. For example, thanks
> to SRFI 9 it is now possible to write quite portable
> code with user-defined types.

I acknowledge your point.

> > I do not think SRFI can take the place of
> >RnRS. During the time I have been reading
> >posts here at comp.lang.scheme (for about a year),
> >I cannot remember any article which refers to SRFI.
> >for resolving a technical issue. All articles /posts
> >refer to R5RS or R4RS.
>
> Actually, a Google search produced quite a number of
> SRFI-related posts, even not counting SRFI announcements.

The fact that there are so many pages that
record the minutes of "meetings" does not necessarily
mean that those meetings solved any problems.

I also did a Google search on SRFI -- Indeed, I saw
many posts, but of those that I saw, I did not see many
that discussed solving "real" technical
problems using particular SRFI features.

Most were discussions on what should be in SRFI, and what
should be the standard, what was submitted, etc., -- in
other words. there were more articles on
SRFI processes and submissions than articles on
using SRFI features.

Now, don't get me wrong here, SRFI
is probably a useful process -- but you have to look at this
in light of RnRS. If I do a search on RnRS, I see far
more technical posts, that relate to standards and
features, rather than posts on what should be
included in RnRS, etc.

> Frankly, I feel that core Scheme is pretty much OK
> and should be left as-is. We need stronger libraries,
> sure. Mostly, these libraries are already available
> in existing Schemes, but there is much divergence.

You are probably far better qualified to make
comments on this point and I. I simply assumed that the
core Scheme needed modifications, because I remembered
that others in this newsgroup had mentioned changes
they thought were necessary.

> [snip]


> >In my view, to produce R6RS or
> >something like it, _and_ to have people feel that the
> >document is the de facto standard, the document
> >must be authored by a committee of respectable
> >Scheme heavyweights.
>
> But what if the Scheme heavyweights don't want to?

Indeed, that is a problem.

> [snip]


> > Or at least, it should have their blessing.
>
> [Having the blessing of the implementors is much more
> important, since they have to do the coding.

To an extent, I agree with you -- "consensus is really
built from the bottom up."

But (IMHO) one can take that view too far and
underestimate the reverse: consensus can also
be "imposed." This is possible, I think, because
implementors want to produce systems that hold
up against a respected standard For example,
when I wrote my interpreter, I did not need to implement
certain R5RS features. But I because I wanted to
produce a R5RS compliant system, I went ahead and
implemented them.


Stephan H.M.J. Houben

unread,
Oct 4, 2001, 4:27:53 AM10/4/01
to
On Wed, 3 Oct 2001 12:09:29 -0400, Ji-Yong D. Chung <virtua...@erols.com>
wrote:

<snip>

>> > I do not think SRFI can take the place of
>> >RnRS. During the time I have been reading
>> >posts here at comp.lang.scheme (for about a year),
>> >I cannot remember any article which refers to SRFI.
>> >for resolving a technical issue. All articles /posts
>> >refer to R5RS or R4RS.
>>
>> Actually, a Google search produced quite a number of
>> SRFI-related posts, even not counting SRFI announcements.
>
> The fact that there are so many pages that
>record the minutes of "meetings" does not necessarily
>mean that those meetings solved any problems.
>
> I also did a Google search on SRFI -- Indeed, I saw
>many posts, but of those that I saw, I did not see many
>that discussed solving "real" technical
>problems using particular SRFI features.

Well, Google puts all the "process" posts first, since
they explicitely have "SRFI" as the very first words.

But I think you have a point. Most SRFI's are not
very important to most people. Which only shows that
most of them probably don't belong in an R6RS.

<snip>

>> Frankly, I feel that core Scheme is pretty much OK
>> and should be left as-is. We need stronger libraries,
>> sure. Mostly, these libraries are already available
>> in existing Schemes, but there is much divergence.
>
> You are probably far better qualified to make
>comments on this point and I.

I suppose you mean that ironically? I'm just a simple
Scheme user who is reasonably happy with the language
as-is.

>I simply assumed that the
>core Scheme needed modifications, because I remembered
>that others in this newsgroup had mentioned changes
>they thought were necessary.

And did you agree with them? Almost all proposed changes
have been vehemently attacked by several people.

<snip>

>> [snip]

You snipped my question about the list of changes
you would like to see incorporated in R6RS.

>> > Or at least, it should have their blessing.
>>
>> [Having the blessing of the implementors is much more
>> important, since they have to do the coding.
>
> To an extent, I agree with you -- "consensus is really
>built from the bottom up."
>
> But (IMHO) one can take that view too far and
>underestimate the reverse: consensus can also
>be "imposed." This is possible, I think, because
>implementors want to produce systems that hold
>up against a respected standard For example,
>when I wrote my interpreter, I did not need to implement
>certain R5RS features. But I because I wanted to
>produce a R5RS compliant system, I went ahead and
>implemented them.

OK, but a new R6RS would only mean more work for you,
as an implementor. So why would you want that?
Or do you feel that people complaining about R5RS
detracts from its status as a standard, and thus
from the status of your Scheme implementation?
I can sympathise with that, but I believe that
any changes to R5RS, producing an R6RS,
would make it more controversial, and thus less useful.

I would love to be proven wrong. Again: can anybody
list uncontroversial additions for a possible R6RS.

Stephan

David Feuer

unread,
Oct 4, 2001, 4:35:42 AM10/4/01
to
"Stephan H.M.J. Houben" wrote:
<snip>

>
> I would love to be proven wrong. Again: can anybody
> list uncontroversial additions for a possible R6RS.

Hmmm... I wonder how much people would object to removing multiple
return values...

--
/Times-Bold 40 selectfont/n{moveto}def/m{gsave true charpath clip 72
400 n 300 -4 1{dup 160 300 3 -1 roll 0 360 arc 300 div 1 1 sethsbcolor
fill}for grestore 0 -60 rmoveto}def 72 500 n(This message has been)m
(brought to you by the)m(letter alpha and the number pi.)m(David Feuer)
m(David...@brown.edu)m showpage

felix

unread,
Oct 4, 2001, 7:11:41 AM10/4/01
to

Stephan H.M.J. Houben wrote in message ...

>
>I would love to be proven wrong. Again: can anybody
>list uncontroversial additions for a possible R6RS.
>


1. Fix that "letrec" bug.
2. Require SRFI-0 support.


cheers,
felix


Michael Sperber [Mr. Preprocessor]

unread,
Oct 4, 2001, 8:11:43 AM10/4/01
to
>>>>> "felix" == felixundduni <felix> writes:

felix> Stephan H.M.J. Houben wrote in message ...


>>
>> I would love to be proven wrong. Again: can anybody
>> list uncontroversial additions for a possible R6RS.
>>

felix> 2. Require SRFI-0 support.

That would be among the *most*, not the least controversial additions.
(Check out the archived discussion, it's all there.) In fact, since
it's tied to the SRFI base, it's pretty much unsuitable and arguably
unnecessary for inclusion in any RnRS.

--
Cheers =8-} Mike
Friede, Völkerverständigung und überhaupt blabla

Ji-Yong D. Chung

unread,
Oct 4, 2001, 9:16:00 AM10/4/01
to
Hi

"Stephan H.M.J. Houben" <ste...@pcrm.win.tue.nl> wrote in message

news:slrn9ro7d3....@pcrm.win.tue.nl...


> On Wed, 3 Oct 2001 12:09:29 -0400, Ji-Yong D. Chung
<virtua...@erols.com>
> wrote:

> >> Frankly, I feel that core Scheme is pretty much OK
> >> and should be left as-is. We need stronger libraries,
> >> sure. Mostly, these libraries are already available
> >> in existing Schemes, but there is much divergence.
> >
> > You are probably far better qualified to make
> >comments on this point and I.

> I suppose you mean that ironically?

No irony intended here.

> You snipped my question about the list of changes
> you would like to see incorporated in R6RS.

I did not answer, not because I meant to ignore
your point. I didn't know the answer.

First, I am not sure what that list should be.
I can dig up the past emails using deja news -- but
I can see where that will lead to. We will be in another
type of discussion loop -- "Is that feature controversial
or not?"

Secondly, I wasn't thinking about radical changes
to R5RS. I was thinking of small changes, rather
than adding new features. (Even though I would like to
see a coherent module system, which I think
is very important for writing a large body of code.)
Perhaps a number of corrections to certain macro
examples, changes to things such as dynamic-wind,
call/cc, call/wc (a la Mathias Blume).

One note: I am not sure, in trying to decide if something
should be included in RnRS, if a features should be deemed
100% uncontroversial. I think simple majority rule is too harsh (51%
overruling 49%), but, on the other hand, 100% agreement
is difficult to come by.

(I was thinking that perhaps 66% majority rule, or 75% majority
rule is better for something like this).

In one sense, this is silly, as it comes down to an arbitrary
numbers game.

> >> > Or at least, it should have their blessing.
> >>
> >> [Having the blessing of the implementors is much more
> >> important, since they have to do the coding.
> >
> > To an extent, I agree with you -- "consensus is really
> >built from the bottom up."
> >
> > But (IMHO) one can take that view too far and
> >underestimate the reverse: consensus can also
> >be "imposed." This is possible, I think, because
> >implementors want to produce systems that hold
> >up against a respected standard For example,
> >when I wrote my interpreter, I did not need to implement
> >certain R5RS features. But I because I wanted to
> >produce a R5RS compliant system, I went ahead and
> >implemented them.
>
> OK, but a new R6RS would only mean more work for you,
> as an implementor. So why would you want that?

On one hand, one has more work -- that is true.

On the other hand, if I knew a feature was used by the
majority, I would make modifications to my implementation,
perhaps with a grumble or two.

As an implementor, I would understand that "standardized"
feature greatly adds to the utility of my product. (Of course,
I would also want the option of being able to add
my own extensions, to distinguish it from other implementations).

> Or do you feel that people complaining about R5RS
> detracts from its status as a standard, and thus
> from the status of your Scheme implementation?

In your earlier post, you mentioned divergence of
library implementations. Partly, I think that is because
SRFI lacks the unifying power, to "impose" a
standard. I felt that something of RnRS's status is
required to make implementors _want_ to revise
their code.

I am not suggesting R6RS should require
a whole set of libraries. Perhaps one or two
would be nice.

> I can sympathise with that, but I believe that
> any changes to R5RS, producing an R6RS,
> would make it more controversial, and thus less useful.

You have a good point here. Consensus is difficult to
build. I have mentioned 66% or 75% majority rule. Well,
I think even 66% majority is extremely difficult to obtain.

If you have 3 people, it is more likely that each person will
have his or her own ideas about how something should be
implemented,

Joe English

unread,
Oct 4, 2001, 11:51:17 AM10/4/01
to
David Feuer wrote:

>"Stephan H.M.J. Houben" wrote:
>>
>> I would love to be proven wrong. Again: can anybody
>> list uncontroversial additions for a possible R6RS.
>
>Hmmm... I wonder how much people would object to removing multiple
>return values...

Or at least replacing 'call-with-values' with 'let-values' as
the distinguished primitive operation.

Personally, I think multiple return values are rather neat;
call-with-values is an abomination though.

(Also: could we please have the old call-with-current-continuation
back, and implement dynamic-wind/call-with-winding-continuation
on top if it?)


--Joe English

jeng...@flightlab.com

felix

unread,
Oct 5, 2001, 5:24:32 AM10/5/01
to

Michael Sperber [Mr. Preprocessor] wrote in message ...

>
>felix> 2. Require SRFI-0 support.
>
>That would be among the *most*, not the least controversial additions.
>(Check out the archived discussion, it's all there.) In fact, since
>it's tied to the SRFI base, it's pretty much unsuitable and arguably
>unnecessary for inclusion in any RnRS.
>


Hm. I have to admit that I don't see that much controversy
in that discussion. "cond-expand" is relatively simple to
implement, is currently the only way of writing (more or less)
portable code, and, most importantly, it's *there*, neatly
specified and already in use.

A standard construct like SRFI-0 is IMHO *badly* needed.

Wether it is politically correct to put a SRFI concept into
an RnRS is something I can not comment on. Perhaps you
can clarify that.


cheers,
felix


David Rush

unread,
Oct 5, 2001, 5:54:02 AM10/5/01
to
"felix" <felixu...@freenet.de> writes:
> Michael Sperber [Mr. Preprocessor] wrote in message ...
> >
> >felix> 2. Require SRFI-0 support.
> >That would be among the *most*, not the least controversial additions.
>
> Hm. I have to admit that I don't see that much controversy
> in that discussion.

Ditto, But Mr. Preprocessor prefers SRFI-7, which has similiar power,
although expressed at a different granularity. IMHO, SRFI-7 is closer
to a module system than a strict portability construct, but
modularization is *also* about portability.

david rush
--
Java is a WORA language! (Write Once, Run Away)
-- James Vandenberg (on prog...@egroups.com)

Ray Dillinger

unread,
Oct 27, 2001, 12:04:10 AM10/27/01
to
Tom Lord <lo...@emf.emf.net> wrote:

: Leaving aside, for the moment, the question of who would write it,


: there ought to be an R6RS.

: The RnRS series is often criticized for being too small -- for leaving
: out features that are needed for a reasonably complete programming
: language. Some notable missing features are an exception mechanism, a
: module system, and a non-toy I/O system.


The problem with the idea of an R6RS is that nothing can go
into an RnRS unless it is both widely implemented and
uncontroversial. The RnRS reports are as much descriptive
as they are prescriptive.

Right now, there is not enough widely implemented, uncontroversial
stuff regarding the language Scheme to support another report.


: Instead, R6RS should aim to provide a non-controversial core language,


: specifying only the details that all reasonable Scheme implementors
: can agree on. To accomplish that, R6RS would have to remove some
: features from R5RS: it should unspecify the result of `(eq? '() #f)';
: it should unspecify whether or not symbols are case sensative.

An implementation in which symbols are case sensitive (and I know
there are some) breaks a lot of existing code. Also, people
remember strings of characters much more easily than they remember
what case each member of the string is in. I bet you're never going
to find it sufficiently uncontroversial to drop that from the
standard. Case-insensitivity is also widely implemented, though
not universal.

Ditto the question of (eq? '() #f) . Admittedly the choice is
at least somewhat arbitrary, but having bitten the bullet and
specified it, people have written code that depends on it. You
will never find it uncontroversial enough to unspecify, and at
this point it is very widely implemented.

At this point, if you want something to be in an RnRS, you need to
be building consensus, not among any standards body, but among the
implementors of scheme systems -- if they all provide something, and
provide it with the exact same semantics, then it will become part
of the next standard. If they all provide different ways of
accomplishing something, or do not provide the something at all,
then it will not be part of a standard. Because it's not going
to be worth getting a standards body together until there's a bunch
of new stuff that's widely implemented and uncontroversial.

Bear

David Rush

unread,
Oct 30, 2001, 11:06:02 AM10/30/01
to
Ray Dillinger <be...@sonic.net> writes:
> Tom Lord <lo...@emf.emf.net> wrote:
> : Leaving aside, for the moment, the question of who would write it,
> : there ought to be an R6RS.

> The problem with the idea of an R6RS is that nothing can go

> into an RnRS unless it is both widely implemented and
> uncontroversial. The RnRS reports are as much descriptive
> as they are prescriptive.
>
> Right now, there is not enough widely implemented, uncontroversial
> stuff regarding the language Scheme to support another report.

I'm asking purely for information, but was dynamic-wind both `widely
implemented' and `uncontroversial' when it got into the language?

david rush
--
In my experience, what every artist wants, really *wants*, is
to be paid.
-- Glod Glodsson, in _Soul Music_

Ray Dillinger

unread,
Oct 31, 2001, 10:58:47 AM10/31/01
to
David Rush <ku...@bellsouth.net> wrote:
: Ray Dillinger <be...@sonic.net> writes:

:> The problem with the idea of an R6RS is that nothing can go

:> into an RnRS unless it is both widely implemented and
:> uncontroversial. The RnRS reports are as much descriptive
:> as they are prescriptive.

: I'm asking purely for information, but was dynamic-wind both `widely


: implemented' and `uncontroversial' when it got into the language?

I could have sworn I responded to this, but the response is nowhere
to be found in my newsreader. I probably hit "email" by mistake.

Anyway, you're right. Dynamic-wind wasn't widely implemented.
However, I'd say the need for it was pretty unambiguous. Here is
why:

If you are passing around objects that have state in an imperative
language, things are pretty simple; you change the state to the one
in which certain things can happen, call routines that can do those
certain things, then change the state back to "normal" and go on.

But scheme is a functional language, with first-class continuations.
That means that the function you call once may get its continuation
captured and return ten times. So you absolutely *HAVE TO* have
some control structure that allows you to change the state of a
stateful object when the continuation which assumes it is in some
state is entered or exited. Well, either that or wrap every bit of
code that touches the object inside a bunch of state checks and
guarantee that a continuation is never captured inside the state
checks....

I don't know in what context dynamic-wind came up at the snowbird
meeting, but I can easily imagine several: File handles are the
first and most important. If your continuation assumes that a file
is open, but you return from the function and close it, then at
some later point when another part of the system invokes the same
continuation it runs straight into a wall when the continuation
either writes to the file, or returns and attempts to close it again.

And the R4RS call/cc had to go away once dynamic-wind was introduced;
otherwise people mixing the two would wind up doing inadvertent and
extremely rude things to their runtime environments.

So anyway, yeah - while it was maybe the biggest surprise to come
out of a standards body in recent memory, I think it was so clearly
the Right Thing that there was really no choice but to have it or
something like it, even though it wasn't widely implemented at the
time.

Bear

Stephan H.M.J. Houben

unread,
Oct 31, 2001, 11:21:01 AM10/31/01
to
On Wed, 31 Oct 2001 15:58:47 GMT, Ray Dillinger <be...@sonic.net> wrote:

>So anyway, yeah - while it was maybe the biggest surprise to come
>out of a standards body in recent memory, I think it was so clearly
>the Right Thing that there was really no choice but to have it or
>something like it, even though it wasn't widely implemented at the
>time.

Except that it is quite obviously not the Right Thing at all,
since it has several major flaws:

1. It is the only construct in the entire Scheme language that
establishes a "dynamic context".
2. It does NOT handle the situation with the file handler in
a satisfactory way. Yes, the file handle is closed, but what
good is it to be reopened if you lose all the other state
on your file, i.e. file position and what not. Worse, somebody
might have deleted the file in the meantime.
3. If I want to use call/cc to implement cooperative multitasking,
I don't want to invoke all those entry- and exit-point handlers
just because I am doing a context switch.
4. It means that call/cc doesn't actually capture the continuation,
but rather something that is almost-but-not-quite the continuation.

As far as I can see, dynamic-wind is only useful if you only ever use
call/cc to implement a primitive exception system.

A much better situation would be:

* a real call/cc which cannot be subverted by dynamic-wind constructs
* a real exception system with unwind-protect handlers

If you throw an exception, then unwind-protect handlers are invoked.
If you call/cc, then nothing happens, since call/cc is for people
who know what they are doing.

Does this mean that one cannot guarantee that a file is closed?
Yes, but that has always been the case, unless your Scheme system happens
to be able to solve the Halting Problem.

Stephan

Matthias Blume

unread,
Oct 31, 2001, 11:15:44 AM10/31/01
to
Ray Dillinger wrote:


There have been voices (e.g., mine) agains dynamic-wind, so it is hardly

uncontroversial. Also, notice that its addition makes a mockery of the
english word "current" in "call-with-CURRENT-continuation". The original
functionality of call/cc has been eliminated because of the addition of
dynamic-wind -- it is no longer possible to capture a "current" continuation.
What you get instead is a "winding" continuation. As has been discussed
to death, both the "current" and the "winding" continuation are worthwhile
having in certain situations. To me, the addition of dynamic-wind is a
real loss because I personally do not care much for winding continuations.

Far better would have been to retain the old call/cc and add two things:
call/wc ("call-with-winding-continuation") and dynamic-wind. As it stands,
there is no good way anymore to implement concurrency on top of call/cc.
(And to me, that's the only real purpose of having it in the first place --
Shriram an Co's web adventures notwithstanding :-) -- because things like
exceptions (the other useful thing to be implemented using call/cc) belong
into the language as a built-in concept. On second thought, concurrency
belongs into the language as well, and call/cc can simply retire, taking
dynamic-wind along, AFAIC...)

--
-Matthias

j...@itasoftware.com

unread,
Oct 31, 2001, 11:50:21 AM10/31/01
to
ste...@pcrm.win.tue.nl (Stephan H.M.J. Houben) writes:

> Except that it is quite obviously not the Right Thing at all,
> since it has several major flaws:

Since I happen to believe it *is* the Right Thing, I'll try to rebut
these objections.

> 1. It is the only construct in the entire Scheme language that
> establishes a "dynamic context".

Not exactly. A piece of running code *has* dynamic context.
Dynamic-wind is the only construct that *manipulates* dynamic
context.

> 2. It does NOT handle the situation with the file handler in
> a satisfactory way. Yes, the file handle is closed, but what
> good is it to be reopened if you lose all the other state
> on your file, i.e. file position and what not. Worse, somebody
> might have deleted the file in the meantime.

Agreed, but I think this is irrelevant except insofar as to eliminate
this example from consideration. There is an external process that
manipulates the file system, so there is a need to synchronize with
it. Dynamic-wind surely helps in synchronization (by `closing' the
file when it is no longer in use), but doesn't solve the problem.

Instead, consider a resource that is completely under control of the
Scheme system. In this case, dynamic-wind is the solution because
there is no external process to synchronize with.

> 3. If I want to use call/cc to implement cooperative multitasking,
> I don't want to invoke all those entry- and exit-point handlers
> just because I am doing a context switch.

If you want to use call/cc to implement cooperative multitasking, you
are living in a state of sin. A multitasking model must have some
notion of a task (a thread of control), using call/cc to `task switch'
means that you are invoking continuations that are not logically part
of the current task.

> 4. It means that call/cc doesn't actually capture the continuation,
> but rather something that is almost-but-not-quite the continuation.

Call/cc generally *doesn't* capture the continuation anyway! It
usually captures a reified image of the interpreter state. This is
wrapped with a function object that restores the state from the
reified image.

> As far as I can see, dynamic-wind is only useful if you only ever use
> call/cc to implement a primitive exception system.
>
> A much better situation would be:
>
> * a real call/cc which cannot be subverted by dynamic-wind constructs
> * a real exception system with unwind-protect handlers
>
> If you throw an exception, then unwind-protect handlers are invoked.
> If you call/cc, then nothing happens, since call/cc is for people
> who know what they are doing.
>
> Does this mean that one cannot guarantee that a file is closed?
> Yes, but that has always been the case, unless your Scheme system happens
> to be able to solve the Halting Problem.

Unfortunately, this breaks the case of dynamic wind where the resource
*is* under control of Scheme. The `open file' example won't work
under any circumstance because of the external world.

Matthias Blume

unread,
Oct 31, 2001, 12:38:56 PM10/31/01
to
j...@itasoftware.com wrote:

> ste...@pcrm.win.tue.nl (Stephan H.M.J. Houben) writes:

>>2. It does NOT handle the situation with the file handler in
>> a satisfactory way. Yes, the file handle is closed, but what
>> good is it to be reopened if you lose all the other state
>> on your file, i.e. file position and what not. Worse, somebody
>> might have deleted the file in the meantime.
>>
>
> Agreed, but I think this is irrelevant except insofar as to eliminate
> this example from consideration. There is an external process that
> manipulates the file system, so there is a need to synchronize with
> it. Dynamic-wind surely helps in synchronization (by `closing' the
> file when it is no longer in use), but doesn't solve the problem.
>
> Instead, consider a resource that is completely under control of the
> Scheme system. In this case, dynamic-wind is the solution because
> there is no external process to synchronize with.


Like, for example, what?


>>3. If I want to use call/cc to implement cooperative multitasking,
>> I don't want to invoke all those entry- and exit-point handlers
>> just because I am doing a context switch.
>>
>
> If you want to use call/cc to implement cooperative multitasking, you
> are living in a state of sin. A multitasking model must have some
> notion of a task (a thread of control), using call/cc to `task switch'
> means that you are invoking continuations that are not logically part
> of the current task.


What else is call/cc good for then?


>>4. It means that call/cc doesn't actually capture the continuation,
>> but rather something that is almost-but-not-quite the continuation.
>>
>
> Call/cc generally *doesn't* capture the continuation anyway! It
> usually captures a reified image of the interpreter state. This is
> wrapped with a function object that restores the state from the
> reified image.


Well, "continuation" is what mathematicians say when they mean "state of
the interpreter".


> Unfortunately, this breaks the case of dynamic wind where the resource
> *is* under control of Scheme. The `open file' example won't work
> under any circumstance because of the external world.


Again: what case? A *real* example, please! And one that is not broken!


--
-Matthias

j...@itasoftware.com

unread,
Oct 31, 2001, 2:09:21 PM10/31/01
to
Matthias Blume <bl...@research.bell-labs.com> writes:

> j...@itasoftware.com wrote:
>
> > ste...@pcrm.win.tue.nl (Stephan H.M.J. Houben) writes:
>
> >>2. It does NOT handle the situation with the file handler in
> >> a satisfactory way. Yes, the file handle is closed, but what
> >> good is it to be reopened if you lose all the other state
> >> on your file, i.e. file position and what not. Worse, somebody
> >> might have deleted the file in the meantime.
> >>
> > Agreed, but I think this is irrelevant except insofar as to eliminate
>
> > this example from consideration. There is an external process that
> > manipulates the file system, so there is a need to synchronize with
> > it. Dynamic-wind surely helps in synchronization (by `closing' the
> > file when it is no longer in use), but doesn't solve the problem.
> > Instead, consider a resource that is completely under control of the
>
> > Scheme system. In this case, dynamic-wind is the solution because
> > there is no external process to synchronize with.
>
>
> Like, for example, what?

Perusing the MIT-Scheme source I find these:

A wrapper that allocates a buffer on entry and clears the contents on
exit.

A wrapper that seizes a lock on entry and relinquishes it on exit.

A wrapper that modifies the state of the drawing mode of the graphics
device. Note that this is an external device with state, but there is


no external process to synchronize with.

A wrapper that keeps track of the depth of recursion when chasing down
file references in the file system (so cycles don't cause problems).

A wrapper that closes a port on exit *and* raises an error if re-entry
is attempted.

A wrapper that dynamically modifies the raw/cooked mode of an open
port.

A wrapper that modifies the current working directory.

>
> >>3. If I want to use call/cc to implement cooperative multitasking,
> >> I don't want to invoke all those entry- and exit-point handlers
> >> just because I am doing a context switch.
> >>
> > If you want to use call/cc to implement cooperative multitasking, you
>
> > are living in a state of sin. A multitasking model must have some
> > notion of a task (a thread of control), using call/cc to `task switch'
> > means that you are invoking continuations that are not logically part
> > of the current task.
>
> What else is call/cc good for then?

Error handling and modeling non-local exit within a thread. (like
common-lisp's BLOCK and RETURN-FROM and CATCH and THROW)

Stephan H.M.J. Houben

unread,
Oct 31, 2001, 2:04:35 PM10/31/01
to
On 31 Oct 2001 11:50:21 -0500, j...@itasoftware.com <j...@itasoftware.com> wrote:
>ste...@pcrm.win.tue.nl (Stephan H.M.J. Houben) writes:
>
>> Except that it is quite obviously not the Right Thing at all,
>> since it has several major flaws:
>
>Since I happen to believe it *is* the Right Thing, I'll try to rebut
>these objections.

OK, let's rebut the rebuttals, in good Usenet tradition.

>> 1. It is the only construct in the entire Scheme language that
>> establishes a "dynamic context".
>
>Not exactly. A piece of running code *has* dynamic context.
>Dynamic-wind is the only construct that *manipulates* dynamic
>context.

Code in Scheme doesn't have a dynamic context, since whatever
a dynamic context is supposed to mean in other languages,
is captured in Scheme in the "current continuation", which
can be stored and re-invoked at will.

>> 2. It does NOT handle the situation with the file handler in
>> a satisfactory way. Yes, the file handle is closed, but what
>> good is it to be reopened if you lose all the other state
>> on your file, i.e. file position and what not. Worse, somebody
>> might have deleted the file in the meantime.
>
>Agreed, but I think this is irrelevant except insofar as to eliminate
>this example from consideration.

Yes, but this was apparently *the* motivating application for
dynamic-wind. The fact that it can't even handle *that*
correctly just shows how utterly broken it is.

>There is an external process that
>manipulates the file system, so there is a need to synchronize with
>it. Dynamic-wind surely helps in synchronization (by `closing' the
>file when it is no longer in use), but doesn't solve the problem.
>
>Instead, consider a resource that is completely under control of the
>Scheme system. In this case, dynamic-wind is the solution because
>there is no external process to synchronize with.

If the resource is completely under control of the Scheme system, just
let it be garbage collected. Things like dynamic-wind are only
ever necessary to deal with external resources in the first place.

>> 3. If I want to use call/cc to implement cooperative multitasking,
>> I don't want to invoke all those entry- and exit-point handlers
>> just because I am doing a context switch.
>
>If you want to use call/cc to implement cooperative multitasking, you
>are living in a state of sin.

Excuse me? But that is *the* prime motivating example of having call/cc
at all!

>A multitasking model must have some
>notion of a task (a thread of control),

No it doesn't, that's the beauty of the Scheme model.
No "dynamic context" is needed in Scheme, so no notion
of a "thread of control" needs to exist. We don't need
no stinkin' thread local storage.

>using call/cc to `task switch'
>means that you are invoking continuations that are not logically part
>of the current task.

Yeah, that's exactly the *point* of call/cc.

I mean, if we don't want to do lightweight threading
and co-routines with call/cc, then we could just as well
only have Common Lisp's return-from and unwind-protect,
and end up with a less powerful, but semantical consistent
language.

>> 4. It means that call/cc doesn't actually capture the continuation,
>> but rather something that is almost-but-not-quite the continuation.
>
>Call/cc generally *doesn't* capture the continuation anyway! It
>usually captures a reified image of the interpreter state. This is
>wrapped with a function object that restores the state from the
>reified image.

Mmm, and (+ 1 2) doesn't produce the number 3, but rather some
bit patterns that are somehow interpreted as 3.

>Unfortunately, this breaks the case of dynamic wind where the resource
>*is* under control of Scheme. The `open file' example won't work
>under any circumstance because of the external world.

I challenge you to produce an example in which unwind-protect
is the Right Thing. I don't believe such an example exists.

Stephan

j...@itasoftware.com

unread,
Oct 31, 2001, 3:04:02 PM10/31/01
to
ste...@pcrm.win.tue.nl (Stephan H.M.J. Houben) writes:

> On 31 Oct 2001 11:50:21 -0500, j...@itasoftware.com <j...@itasoftware.com> wrote:
> >ste...@pcrm.win.tue.nl (Stephan H.M.J. Houben) writes:
> >
> >> Except that it is quite obviously not the Right Thing at all,
> >> since it has several major flaws:
> >
> >Since I happen to believe it *is* the Right Thing, I'll try to rebut
> >these objections.
>
> OK, let's rebut the rebuttals, in good Usenet tradition.
>
> >> 1. It is the only construct in the entire Scheme language that
> >> establishes a "dynamic context".
> >
> >Not exactly. A piece of running code *has* dynamic context.
> >Dynamic-wind is the only construct that *manipulates* dynamic
> >context.
>
> Code in Scheme doesn't have a dynamic context, since whatever
> a dynamic context is supposed to mean in other languages,
> is captured in Scheme in the "current continuation", which
> can be stored and re-invoked at will.

Let's separate the issues of dynamic context from continuations for
the moment. I think you'll grant me that there is a notion of dynamic
state in a program --- it starts before it ends and it proceeds
through some number of states inbetween. It is this state that I am
calling the dynamic context.

This state isn't captured in the current continuation (if it were,
then side effects would be undone when the continuation was invoked).

> >> 2. It does NOT handle the situation with the file handler in
> >> a satisfactory way. Yes, the file handle is closed, but what
> >> good is it to be reopened if you lose all the other state
> >> on your file, i.e. file position and what not. Worse, somebody
> >> might have deleted the file in the meantime.
> >
> >Agreed, but I think this is irrelevant except insofar as to eliminate
> >this example from consideration.
>
> Yes, but this was apparently *the* motivating application for
> dynamic-wind. The fact that it can't even handle *that*
> correctly just shows how utterly broken it is.

I disagree. Consider this code:

(define (call-with-open-file file-specifier thunk)
(let ((first-time #t)
(stream '()))
(dynamic-wind (lambda () (if first-time
(begin (set! first-time #f)
(set! stream (open file-specifier ...)))
(error "Non-reentrant")))
(thunk stream)
(lambda () (close stream)))))

I claim that the problem to be solved is

1. Closing the file no matter how the form is exited.

2. Preventing re-use of the continuation because the external file
system doesn't support it.

The above code performs that function.

> >There is an external process that
> >manipulates the file system, so there is a need to synchronize with
> >it. Dynamic-wind surely helps in synchronization (by `closing' the
> >file when it is no longer in use), but doesn't solve the problem.
> >
> >Instead, consider a resource that is completely under control of the
> >Scheme system. In this case, dynamic-wind is the solution because
> >there is no external process to synchronize with.
>
> If the resource is completely under control of the Scheme system, just
> let it be garbage collected. Things like dynamic-wind are only
> ever necessary to deal with external resources in the first place.

An external resource can be `under the control' of the Scheme system,
yet need dynamic-wind. Consider the case of the `current directory'
in a Unix system. The process will have one, and you might wish to
dynamically modify it, but without dynamic-wind, there is no way to
safely temporarily alter the value. GC won't help.

> >> 3. If I want to use call/cc to implement cooperative multitasking,
> >> I don't want to invoke all those entry- and exit-point handlers
> >> just because I am doing a context switch.
> >
> >If you want to use call/cc to implement cooperative multitasking, you
> >are living in a state of sin.
>
> Excuse me? But that is *the* prime motivating example of having call/cc
> at all!

Oh? I thought call/cc was a generalized version of catch and throw.
If you look at the history of the RnRS reports, you'll see that the
original scheme report had catch and throw operators. Extending these
to be `first-class' requires that they have indefinite extent.

Call/cc is simply unsuited for emulation of a multitasking system.

> >A multitasking model must have some
> >notion of a task (a thread of control),
>
> No it doesn't, that's the beauty of the Scheme model.

Er, when you say `multitasking', what is it exactly you have
`multi'ple of?

> No "dynamic context" is needed in Scheme, so no notion
> of a "thread of control" needs to exist.

Again, this begs the question of what on earth you are talking about
if not about multiple threads of control.

> We don't need no stinkin' thread local storage.

What has this to do with anything?

> >using call/cc to `task switch'
> >means that you are invoking continuations that are not logically part
> >of the current task.
>
> Yeah, that's exactly the *point* of call/cc.

No, the point is to model non-local exits.

> I mean, if we don't want to do lightweight threading
> and co-routines with call/cc, then we could just as well
> only have Common Lisp's return-from and unwind-protect,
> and end up with a less powerful, but semantical consistent
> language.

Exactly. Though call/cc is more powerful (and general) than
catch/throw or return-from.

> >> 4. It means that call/cc doesn't actually capture the continuation,
> >> but rather something that is almost-but-not-quite the continuation.
> >
> >Call/cc generally *doesn't* capture the continuation anyway! It
> >usually captures a reified image of the interpreter state. This is
> >wrapped with a function object that restores the state from the
> >reified image.
>
> Mmm, and (+ 1 2) doesn't produce the number 3, but rather some
> bit patterns that are somehow interpreted as 3.

To be pedantic, yes. Look at the implementation of call/cc in your
favorite scheme system. There is a lot more under the hood than
simply copying the current value of the PC and SP.

> >Unfortunately, this breaks the case of dynamic wind where the resource
> >*is* under control of Scheme. The `open file' example won't work
> >under any circumstance because of the external world.
>
> I challenge you to produce an example in which unwind-protect
> is the Right Thing. I don't believe such an example exists.

The examples I posted to Matthias should suffice.

Matthias Blume

unread,
Oct 31, 2001, 3:40:37 PM10/31/01
to
j...@itasoftware.com wrote:


> I disagree. Consider this code:
>
> (define (call-with-open-file file-specifier thunk)
> (let ((first-time #t)
> (stream '()))
> (dynamic-wind (lambda () (if first-time
> (begin (set! first-time #f)
> (set! stream (open file-specifier ...)))
> (error "Non-reentrant")))
> (thunk stream)
> (lambda () (close stream)))))
>
> I claim that the problem to be solved is
>
> 1. Closing the file no matter how the form is exited.
>
> 2. Preventing re-use of the continuation because the external file
> system doesn't support it.
>
> The above code performs that function.


This is just a clumsy hack. What one really wants is:

1. Being able to use the file whenever the corresponding port is
accessible.
2. Having the file closed when the port cannot possibly be accessed
anymore.


> An external resource can be `under the control' of the Scheme system,
> yet need dynamic-wind. Consider the case of the `current directory'
> in a Unix system. The process will have one, and you might wish to
> dynamically modify it, but without dynamic-wind, there is no way to
> safely temporarily alter the value. GC won't help.


Wrong. You can construct an abstract type "working_directory" that you
pass around. Functions (such as "open") that implicitly read the current
working directory will receive an explicit working_directory argument.
It is not hard to optimize this enough so that you change the actual
current working directory only if you really have to.
This, IMO, is the Right Design(tm).


> Oh? I thought call/cc was a generalized version of catch and throw.


This may very well be. But the real question is: what is it good for?
Stephen's (and my) claim is that implementing concurrency is the only
plausible answer to this question.


> Call/cc is simply unsuited for emulation of a multitasking system.


This is complete and utter BS. Some of the best concurrent languages
have been implemented on top of call/cc.


> No, the point is to model non-local exits.


If you need non-local exits, then your language should better provide them
directly (e.g., in form of exceptions). That's cleaner, simpler, does not
require call/cc or dynamic-wind, and does not force everyone to reinvent
the wheel every other day.

Call/cc is *way* too big a hammer for this tiny nail!


> To be pedantic, yes. Look at the implementation of call/cc in your
> favorite scheme system. There is a lot more under the hood than
> simply copying the current value of the PC and SP.


Oh, really? Well, yes, you need to save the other registers, too, of
course. But other than that, *my* favorite system (not Scheme, though)
does precisely that: save all the live registers including SP and PC,
nothing more.


> The examples I posted to Matthias should suffice.


Sorry, they don't. They all fall into the "ugly hack" category.

--
-Matthias

Thant Tessman

unread,
Oct 31, 2001, 4:04:25 PM10/31/01
to
Matthias Blume wrote:

> [...] What one really wants is:


>
> 1. Being able to use the file whenever the corresponding port is
> accessible.
> 2. Having the file closed when the port cannot possibly be accessed
> anymore.

Doen't plain old Scheme (with call/cc) plus guardians give you this? Or
is there some problem with writing to a port from separate co-routines
that I'm not taking into account (other than the obvious design flaws
like that one co-routine might close the port).

-thant (trying to understand what the argument is even about)

Matthias Blume

unread,
Oct 31, 2001, 4:12:14 PM10/31/01
to
Thant Tessman wrote:


Precisely. I was talking about finalization, but I avoided mentioning

the word.

Clearly (to me, at least), finalization is the Right Thing(tm) here.
The solution that jrm is proposing (that is: generating a runtime error when
you enter a certain dynamic scope where you might have access to a closed port)
is a gross hack. If you are willing to rely on runtime errors, why not
just wait for the one you will get when you actually try to do something
with the closed port?!?


Anyway, I think we beat this horse to death...

--
-Matthias

Ozan Yigit

unread,
Oct 31, 2001, 8:12:11 PM10/31/01
to
j...@itasoftware.com writes:

> Call/cc is simply unsuited for emulation of a multitasking system.

this is not true. there are several well-documented examples; you
may start with the scheme bibliography for some pointers. there may be
other reasons why scheme may work well for multitasking, but call/cc
is not one of them.

> Look at the implementation of call/cc in your favorite scheme system.
> There is a lot more under the hood than simply copying the current value
> of the PC and SP.

well, i implemented it several times with a small vm, and there is not
a hell of a lot to it. dybvig's thesis shows how to do it in a virtual
machine with two special opcodes conti and nuate. the interesting part
is to how to do a fast call/cc, for which clinger's report is still
the required read.

oz
--
www.cs.yorku.ca/~oz | if you couldn't find any weirdness, maybe
york u. computer science | we'll just have to make some! -- hobbes

Jeffrey Siegal

unread,
Nov 1, 2001, 1:22:43 AM11/1/01
to
Matthias Blume wrote:

> This is just a clumsy hack. What one really wants is:
>
> 1. Being able to use the file whenever the corresponding port is
> accessible.
> 2. Having the file closed when the port cannot possibly be accessed
> anymore.

That's likely true in a Scheme-only world, but when interacting with a
non-Scheme world, you don't necessarily want to equate accessability
with open-ness (or other external resource lifetime).


David Feuer

unread,
Nov 1, 2001, 1:58:27 AM11/1/01
to

I can understand wanting to close the file even if the port is
accessible, but why would anyone want the file to be open when the port
is inaccessible?

Jeffrey Siegal

unread,
Nov 1, 2001, 2:28:19 AM11/1/01
to
David Feuer wrote:

> Jeffrey Siegal wrote:
>
>>Matthias Blume wrote:
>>
>>
>>>This is just a clumsy hack. What one really wants is:
>>>
>>>1. Being able to use the file whenever the corresponding port is
>>> accessible.
>>>2. Having the file closed when the port cannot possibly be accessed
>>> anymore.
>>>
>>That's likely true in a Scheme-only world, but when interacting with a
>>non-Scheme world, you don't necessarily want to equate accessability
>>with open-ness (or other external resource lifetime).
>>
>
> I can understand wanting to close the file even if the port is
> accessible, but why would anyone want the file to be open when the port
> is inaccessible?

In general, I think you wouldn't, in which case you could take my
objection as applying only to 1 and not 2 above. The practical effect of
this is the same -- you need to have an explicit close operator, rather
than just using finalization, which means there will be accessible ports
in a "closed" state.

Hypothetically, though, I could imagine a case where you might want to
leave the file open (but without any further need to access it) as long
as the program is running and let the operating system (or run time
system) close it when the program exits, perhaps abnormally.

Stephan H.M.J. Houben

unread,
Nov 1, 2001, 3:05:35 AM11/1/01
to

Yes, but this was explicitely in the discussion of a pure Scheme system.

A pure Scheme system might resemble the Smalltalk LOOM (Large Object
Oriented Memory) system, where individual objects can be pages in
and out to disk. A conventional file system can be emulated on top of
this by implementing files as a kind of strings and directories as
hastables mapping names on files.

The underlying system would make periodic snapshots to disk so that
a crash doesn't loose your work; i.e. "orthogonal persistency" a la
EROS.

In such a system, files are garbage collected just as everything else.
A file (port) doesn't have to be closed; you just drop the reference
and it will become candidate for GC.

Of course, in the Real World (TM) we don't have such a system (even though
the EROS people claim it is more efficient than a conventional file system).

Stephan

David Rush

unread,
Nov 1, 2001, 3:37:36 AM11/1/01
to

Methinks you are missing the point. The point is that it is a
*feature* of R4RS call/cc that it doesn't do all the winding magic. As
Matthias (and others) have shown call/wc (R5RS call/cc) can be
implemented entirely in terms of call/cc, but not vice-versa. This
makes call/cc *more fundamental* than call/wc, and therefore the bit
that is supposed to be in the standard. Building a language from
small, orthogonal concepts is a Good Thing (TM). All that winding
magic is just a control structure built out of the fundamental control
operator call/cc.

Bring back R4RS call/cc!

david rush
--
In a profession plagued by, "when all you have is a hammer, everything
looks like a nail," we get really excited when someone is able to come
along and prove that everything really *is* a nail if lambda is the
hammer. -- Bruce R Lewis (on comp.lang.scheme)

David Rush

unread,
Nov 1, 2001, 3:45:48 AM11/1/01
to
j...@itasoftware.com writes:
> Look at the implementation of call/cc in your
> favorite scheme system. There is a lot more under the hood than
> simply copying the current value of the PC and SP.

But there doesn't have to be; in fact, it can be even less. Call/cc
can be implemented entirely in terms of (simple) source code
transformations. In fact, because Scheme has proper tail-call
behavior, you can program directly in CPS and avoid the issue entirely
(at the cost of having nearly completely unreadable code).

The point of having call/cc in the language is to make this source
transformation readily available to programmers. call/wc mostly
destroyed that.

Bring back R4RS call/cc!

david rush
--
Lambda calculus -- Call us a mad club.
-- James A Crippen (on comp.lang.scheme)

David Rush

unread,
Nov 1, 2001, 4:08:51 AM11/1/01
to
Matthias Blume <bl...@research.bell-labs.com> writes:
> The solution that jrm is proposing (that is: generating a runtime error when
> you enter a certain dynamic scope where you might have access to a closed port)
> is a gross hack. If you are willing to rely on runtime errors, why not
> just wait for the one you will get when you actually try to do something
> with the closed port?!?

Or even work on providing a good mechanism to allow user-space code to
catch and handle errors detected by the runtime system. The RnRS
semantics for 'an error is signalled' are apalling.

> Anyway, I think we beat this horse to death...

Apparently it still has *some* life...

WHAM!

David Rush

unread,
Nov 1, 2001, 4:27:42 AM11/1/01
to
ste...@pcrm.win.tue.nl (Stephan H.M.J. Houben) writes:
> A pure Scheme system might resemble the Smalltalk LOOM (Large Object
> Oriented Memory) system, where individual objects can be pages in
> and out to disk. A conventional file system can be emulated on top of
> this by implementing files as a kind of strings and directories as
> hastables mapping names on files.
>
> The underlying system would make periodic snapshots to disk so that
> a crash doesn't loose your work; i.e. "orthogonal persistency" a la
> EROS.

I've always thought this sort of thing was quite cool. Do you have any
pointers to papers?

> Of course, in the Real World (TM) we don't have such a system (even though
> the EROS people claim it is more efficient than a conventional file system).

I'm just waiting for the right Scheme implementation before writing it
;)

david rush
--
Java and C++ make you think that the new ideas are like the old ones.
Java is the most distressing thing to hit computing since MS-DOS.
-- Alan Kay

Stephan H.M.J. Houben

unread,
Nov 1, 2001, 5:38:23 AM11/1/01
to
On 01 Nov 2001 09:27:42 +0000, David Rush <ku...@bellsouth.net> wrote:
>ste...@pcrm.win.tue.nl (Stephan H.M.J. Houben) writes:
>> A pure Scheme system might resemble the Smalltalk LOOM (Large Object
>> Oriented Memory) system, where individual objects can be pages in
>> and out to disk. A conventional file system can be emulated on top of
>> this by implementing files as a kind of strings and directories as
>> hastables mapping names on files.
>>
>> The underlying system would make periodic snapshots to disk so that
>> a crash doesn't loose your work; i.e. "orthogonal persistency" a la
>> EROS.
>
>I've always thought this sort of thing was quite cool. Do you have any
>pointers to papers?

LOOM is described in "Smalltalk 80, Bits of History, Words of Advice",
ISBN 0-201-11669-3.
Unfortunately, this is hard to get.

A paper about implementing such things is:

Paul R. Wilson: Pointer Swizzling at Page Fault Time:
Efficiently Supporting Huge Address Spaces on Standard
Hardware. ACM Computer Architecture News, June 1991: 6-13

Note that so-called "Object-oriented databases" are in fact applications
of similar techniques. As far as the programmer is concerned, all objects
are in memory. The system brings them from disk transparently.

Papers on EROS are available from:
http://www.eros-os.org/devel/00Devel.html

>> Of course, in the Real World (TM) we don't have such a system (even though
>> the EROS people claim it is more efficient than a conventional file system).
>
>I'm just waiting for the right Scheme implementation before writing it

Perhaps the MzScheme kernel? Seriously, a true "object database" on
top of an existing Scheme system could be a killer app.

Stephan

felix

unread,
Nov 1, 2001, 5:35:56 AM11/1/01
to

Matthias Blume wrote in message <3BE03730...@research.bell-labs.com>...

>
>What else is call/cc good for then?
>


Non-local exits, exceptions and backtracking, for example.
And you can model other languages' control structures easily within
Scheme. Of course one could add a distinct primitive form for
each and every use of it. But why, when call/cc gives you
everything in a single construct?

(BTW, I find it hard to believe that you really think you can anticipate
every possible use of call/cc)


cheers,
felix


j...@itasoftware.com

unread,
Nov 1, 2001, 9:58:26 AM11/1/01
to
Ozan Yigit <o...@blue.cs.yorku.ca> writes:

> j...@itasoftware.com writes:
>
> > Call/cc is simply unsuited for emulation of a multitasking system.
>
> this is not true. there are several well-documented examples; you
> may start with the scheme bibliography for some pointers. there may be
> other reasons why scheme may work well for multitasking, but call/cc
> is not one of them.

R5RS call/cc (which does dynamic winding) is clearly not suitable for
multitasking. I think we all agree with that.

Now I agree that one can kludge a co-operative multitasking model
using call/cc. One can even kludge a pre-emptive multitasking model
using call/cc and timer interrupts. But this is *only* an emulation
of one particular *implementation* of multitasking; to wit:
time-division multiplexing.

This is a very popular form of multitasking, but it is not the only
form. It is easy to imagine writing a dual-processor version of
Scheme. The semantics of invoking a continuation captured by the
other processor is muddy at best (and I think nonsensical). This
extends to a model where the OS is emulating multiple threads via
time-division multiplexing. It extends to a model where Scheme is
emulating time-division multiplexing itself.

> > Look at the implementation of call/cc in your favorite scheme system.
> > There is a lot more under the hood than simply copying the current value
> > of the PC and SP.
>
> well, i implemented it several times with a small vm, and there is not
> a hell of a lot to it.

I've done it myself. However, I did it as a two step process. The
underlying VM had a way of transferring control to other threads by
replacing the processor state. This was exposed to the user as two
different interfaces: one for manipulating virtual threads, the other
for manipulating the control paths within a single thread.

> The interesting part is to how to do a fast call/cc, for which
> Clinger's report is still the required read.

I used Baker's method of never returning. Call/cc took essentially no
time whatsoever.

Richard Cobbe

unread,
Nov 1, 2001, 10:03:12 AM11/1/01
to
Matthias Blume <bl...@research.bell-labs.com> writes:

> j...@itasoftware.com wrote:
>
> > An external resource can be `under the control' of the Scheme system,
> > yet need dynamic-wind. Consider the case of the `current directory'
> > in a Unix system. The process will have one, and you might wish to
> > dynamically modify it, but without dynamic-wind, there is no way to
> > safely temporarily alter the value. GC won't help.
>
>
> Wrong. You can construct an abstract type "working_directory" that you
> pass around. Functions (such as "open") that implicitly read the current
> working directory will receive an explicit working_directory argument.
> It is not hard to optimize this enough so that you change the actual
> current working directory only if you really have to.
> This, IMO, is the Right Design(tm).

Is it? For a concrete example, consider MzScheme's parameters, which
control things like the current working directory and current exception
handler. MzScheme provides a parameterize form to make working with these
more convenient:

(parameterize
((current-directory "/foo/bar/baz"))
body ...)

This evaluates the body... expressions in a context in which the value of
the current-directory parameter is "/foo/bar/baz". Specifically, the
parameterize form remembers the current value of the parameter(s) and swaps
the current and remembered values upon entry into or exit from the body
expressions. Behind the scenes, of course, it most likely uses
dynamic-wind or something equivalent to accomplish this.

The idea of supplying the values of these parameters as normal function
arguments is a cleaner design, I'll agree. It would probably simplify
MzScheme's implementation too, since they wouldn't have to mess with all
the thread-local storage stuff (a parameter's value is independent in each
MzScheme thread).

But. MzScheme v103 predefines 38 such parameters, which range from the
current working directory to aspects of the reader and printer to the
current exception handler. (See the MzScheme docs, in the `Threading'
section, for a complete list.) It would rapidly get very unwieldy to keep
passing these 38 values around to all of your functions.

Here, at least, I think there's an argument for using dynamic-wind for
convenience and readability. (And, this avoids what I understand to be the
primary complaint about dynamic-wind's current design (as opposed to its
existence): neither the on-entry nor on-exit thunks invoke or capture
continuations, so the lack of well-specified behavior in those cases isn't
an issue for this example.)

Richard

j...@itasoftware.com

unread,
Nov 1, 2001, 10:07:53 AM11/1/01
to
Matthias Blume <bl...@research.bell-labs.com> writes:

> j...@itasoftware.com wrote:
>
> > I claim that the problem to be solved is
> > 1. Closing the file no matter how the form is exited.
>
> > 2. Preventing re-use of the continuation because the external file
>
> > system doesn't support it.
> > The above code performs that function.
>
> This is just a clumsy hack. What one really wants is:
>
> 1. Being able to use the file whenever the corresponding port is
> accessible.
> 2. Having the file closed when the port cannot possibly be accessed
> anymore.

Well, if finalization were more reliable and predictable, yes.
Unfortunately, you cannot count on finalization to dispose of the
object in a timely manner. (Not to say that you shouldn't use a
finalizer as a `safety net')

> > An external resource can be `under the control' of the Scheme system,
> > yet need dynamic-wind. Consider the case of the `current directory'
> > in a Unix system. The process will have one, and you might wish to
> > dynamically modify it, but without dynamic-wind, there is no way to
> > safely temporarily alter the value. GC won't help.
>
>
> Wrong. You can construct an abstract type "working_directory" that you
> pass around. Functions (such as "open") that implicitly read the current
> working directory will receive an explicit working_directory argument.
> It is not hard to optimize this enough so that you change the actual
> current working directory only if you really have to.
> This, IMO, is the Right Design(tm).

I disagree. There is a place and use for global state, and I think
that the current `working directory' may qualify (I don't want to say
that it *does* qualify, there may be better candidates).

> > Oh? I thought call/cc was a generalized version of catch and throw.
>
> This may very well be. But the real question is: what is it good for?
> Stephen's (and my) claim is that implementing concurrency is the only
> plausible answer to this question.

My claim is that implementing advanced control constructs such as
exceptions and catch and throw is the only plausible answer.

> > Call/cc is simply unsuited for emulation of a multitasking system.
>
> This is complete and utter BS. Some of the best concurrent languages
> have been implemented on top of call/cc.

Yes, you can kludge a time-division multiplex model of concurrency
with call/cc.

> > No, the point is to model non-local exits.
>
> If you need non-local exits, then your language should better provide them
> directly (e.g., in form of exceptions). That's cleaner, simpler, does not
> require call/cc or dynamic-wind, and does not force everyone to reinvent
> the wheel every other day.
>
> Call/cc is *way* too big a hammer for this tiny nail!

The reason that Scheme doesn't define an exception system or non-local
exits is that it doesn't have to. Call/cc is there to provide you the
underlying mechanism.

> > The examples I posted to Matthias should suffice.
>
> Sorry, they don't. They all fall into the "ugly hack" category.

All of them? How would you do them?

j...@itasoftware.com

unread,
Nov 1, 2001, 10:12:58 AM11/1/01
to
David Rush <ku...@bellsouth.net> writes:

> Methinks you are missing the point. The point is that it is a
> *feature* of R4RS call/cc that it doesn't do all the winding magic. As
> Matthias (and others) have shown call/wc (R5RS call/cc) can be
> implemented entirely in terms of call/cc, but not vice-versa.

Actually, this isn't quite true. R5RS can be implemented in terms of
R4RS only a) at the expense of removing the guarantees of dynamic
wind, or b) requiring everyone to co-operate by using the r5rs
version.

> This makes call/cc *more fundamental* than call/wc, and therefore
> the bit that is supposed to be in the standard. Building a language
> from small, orthogonal concepts is a Good Thing (TM). All that
> winding magic is just a control structure built out of the
> fundamental control operator call/cc.

It isn't really a question of which call/cc, but rather a question of
whether dynamic-wind should exist. Without it, you don't have
unwind-protection, and you have no way of implementing it reliably.

Ray Dillinger

unread,
Nov 1, 2001, 11:40:28 AM11/1/01
to

This is the most compelling argument I've seen so far.

On the other hand, you can implement everything with a
turing machine -- but that doesn't make it the right
choice for a language.

Still, I've seen two compelling arguments here: if call/wc
can be implemented in terms of R4RS call/cc, and the two can
be used in the same program without doing rude things to the
environment, that's a very compelling argument. Second, if you
want to use call/cc in a context where the continuations are
extremely heavy (such as context-switching between different
programs) but don't want to be manipulating object state
thousands of times more often than necessary (ie, open that
darn pipe for writing every time you enter the continuation
and close it when you leave, even if you visit the continuation
a thousand times before an event that causes the pipe to be
used happens....) that's also a very compelling argument.

Hmmm. Clearly, if call/wc isn't the right thing, maybe we need
to think very hard about the approach to protecting mutable
objects we want to use.

In particular, if we want to get down into the guts of machine
control, we have to have structures capable of handling mutable
objects. Memory-mapped I/O for peripherals, for instance.
Synching up with other processes is secondary; first we have
to be able to control mutable objects we own.

Bear

Ray Dillinger

unread,
Nov 1, 2001, 12:00:14 PM11/1/01
to
j...@itasoftware.com wrote:
>
> Matthias Blume <bl...@research.bell-labs.com> writes:

> > This is just a clumsy hack. What one really wants is:
> >
> > 1. Being able to use the file whenever the corresponding port is
> > accessible.
> > 2. Having the file closed when the port cannot possibly be accessed
> > anymore.
>
> Well, if finalization were more reliable and predictable, yes.
> Unfortunately, you cannot count on finalization to dispose of the
> object in a timely manner. (Not to say that you shouldn't use a
> finalizer as a `safety net')

Depending on the GC system, you can. I remember that in
some schemes, objects that don't contain references to
other objects (characters, strings, and more importantly
file handles) are guaranteed to get collected instantly
when the last pointer to them is dropped. It's actually
pretty easy to refcount such objects fairly efficiently and
do instant-collection. With lists, vectors, etc, refcounting
is useless because you have to implement some other GC
anyway. But all the objects that require finalization are
primitive objects and can be collected with optimized
refcounts, which enables guarantees of timeliness.

Bear

Thant Tessman

unread,
Nov 1, 2001, 12:08:34 PM11/1/01
to
j...@itasoftware.com wrote:


[...]

> Well, if finalization were more reliable and predictable, yes.
> Unfortunately, you cannot count on finalization to dispose of the
> object in a timely manner. (Not to say that you shouldn't use a
> finalizer as a `safety net')

It's very strange reading this here. I've read this argument several
times in comp.lang.c++.moderated--only there it's used as an argument
against the utility of automatic memory management because C++
programmers tend not to distinguish between the specific problem of
memory management and the general problem of resource management.


[...]

-thant

David Rush

unread,
Nov 1, 2001, 12:12:23 PM11/1/01
to
Richard Cobbe <co...@airmail.net> writes:
> Matthias Blume <bl...@research.bell-labs.com> writes:
> > j...@itasoftware.com wrote:
> >
> > > An external resource can be `under the control' of the Scheme system,
> > > yet need dynamic-wind. Consider the case of the `current directory'
> > > in a Unix system. The process will have one, and you might wish to
> > > dynamically modify it, but without dynamic-wind, there is no way to
> > > safely temporarily alter the value. GC won't help.
> >
> >
> > Wrong. You can construct an abstract type "working_directory" that you
> > pass around. Functions (such as "open") that implicitly read the current
> > working directory will receive an explicit working_directory argument.
> > It is not hard to optimize this enough so that you change the actual
> > current working directory only if you really have to.
> > This, IMO, is the Right Design(tm).

Absolutely. Think monads, folks.

> But. MzScheme v103 predefines 38 such parameters, It would rapidly


> get very unwieldy to keep passing

But you *don't* actually use all 38 eveywhere, so you only ever need to
pass a subset.

> Here, at least, I think there's an argument for using dynamic-wind for
> convenience and readability.

Nolo contendre, actually. It's just that call/wc can be implemented
interms of call/cc. This makes replacing call/cc with call/wc wrong.

david rush
--
And Visual Basic programmers should be paid minimum wage :)
-- Jeffrey Straszheim (on comp.lang.functional)

David Rush

unread,
Nov 1, 2001, 12:18:15 PM11/1/01
to
j...@itasoftware.com writes:

> time-division multiplexing.
>
> This is a very popular form of multitasking, but it is not the only
> form. It is easy to imagine writing a dual-processor version of
> Scheme. The semantics of invoking a continuation captured by the
> other processor is muddy at best (and I think nonsensical).

Sorry. I don't agree. Invoking a captured continuation is the same
thing as invoking any other function. The fact that call/cc is often
implemented by a stack capture doesn't change that fact. The
stack-capturing implementations need to change; not the semantics of
call/cc.

> I used Baker's method of never returning. Call/cc took essentially no
> time whatsoever.

Which would definitely lead to your problems in a multi-processor
environment. All of which goes to show that Baker's method of
continuation capture is inadequate. But what do you expect? call/cc is
essentially a *syntactic* operation. Implementing it at run-time is
only going to cause problems.

david rush
--
They that can give up essential liberty to obtain a little temporary
safety deserve neither liberty nor safety.
-- Benjamin Franklin

David Feuer

unread,
Nov 1, 2001, 12:29:26 PM11/1/01
to
> david rush
> --
> They that can give up essential liberty to obtain a little temporary
> safety deserve neither liberty nor safety.
> -- Benjamin Franklin

Seems relevant to the current thread...

Matthias Blume

unread,
Nov 1, 2001, 2:18:38 PM11/1/01
to
felix wrote:

> Matthias Blume wrote in message <3BE03730...@research.bell-labs.com>...
>
>>What else is call/cc good for then?
>>
>>
>
>
> Non-local exits,


Call/cc is not *good* for non-local exits. Yes, you can use it to
implement them, but I no longer consider that a "good" thing.
Exceptions are good for non-local exits.

> exceptions


Well, I don't see the difference between exceptions and non-local exits.
So see above.

> and backtracking,


Backtracking can also be done simply using exceptions. Or explicit CPS.


> And you can model other languages' control structures easily within
> Scheme.


But that's not necessarily a good thing. Call/cc is such a powerful construct,
it lets you model pretty much *any* other control construct -- including
the very messy ones. That's precisely the problem.

Don't get me wrong: call/cc in some form is a great tool for implementing
the things that you list. But that does not mean that you have to expose
it to the programmer. After all, we all know how useful raw memory access
or address arithmetic are for implementing things, but Scheme and other
HLLs are quite clear in that they do not want to expose those.

> Of course one could add a distinct primitive form for
> each and every use of it. But why, when call/cc gives you
> everything in a single construct?


See above. To say it slightly differently: Because call/cc can be used in
very undisciplined ways. All of the uses that you list or that one could
come up with actually correspond to rather disciplined uses of call/cc.
Implementations can be simplified by taking advantage of such discipline.
But once call/cc has been exposed to the programmer, such simplification
is no longer possible because discipline is not guaranteed anymore.


> (BTW, I find it hard to believe that you really think you can anticipate
> every possible use of call/cc)


You don't have to believe it because I don't think that. But is it worth
catering to "every possible use" of something most programmers have a hard
time grasping? They have a hard time for a reason: call/cc really *is* a
rather puzzling thing.

Over the years I simply have not seen *any* interesting uses of it that are
not better captured by their respective separate language constructs. The only
other use of call/cc is to make mind-bending puzzles such as
((call/cc call/cc) (call/cc call/cc)) etc. I don't think that being able to
do *that* is worth the trouble.

--
-Matthias

Matthias Blume

unread,
Nov 1, 2001, 2:29:41 PM11/1/01
to
j...@itasoftware.com wrote:

> Ozan Yigit <o...@blue.cs.yorku.ca> writes:
>
>
>>j...@itasoftware.com writes:
>>
>>
>>>Call/cc is simply unsuited for emulation of a multitasking system.
>>>
>>this is not true. there are several well-documented examples; you
>>may start with the scheme bibliography for some pointers. there may be
>>other reasons why scheme may work well for multitasking, but call/cc
>>is not one of them.
>>
>
> R5RS call/cc (which does dynamic winding) is clearly not suitable for
> multitasking. I think we all agree with that.
>
> Now I agree that one can kludge a co-operative multitasking model
> using call/cc. One can even kludge a pre-emptive multitasking model
> using call/cc and timer interrupts. But this is *only* an emulation
> of one particular *implementation* of multitasking; to wit:
> time-division multiplexing.
>
> This is a very popular form of multitasking, but it is not the only
> form. It is easy to imagine writing a dual-processor version of
> Scheme. The semantics of invoking a continuation captured by the
> other processor is muddy at best (and I think nonsensical). This
> extends to a model where the OS is emulating multiple threads via
> time-division multiplexing. It extends to a model where Scheme is
> emulating time-division multiplexing itself.

I completely disagree. A continuation is -- in concrete terms -- the state
of control of a process/thread. To me it makes perfect sense for one CPU
to take such a state and re-instate it -- even if the state was captured
on another CPU. At least in a memory-shared world this makes perfect sense.
In fact, SMP operating systems do precisely that.

And having multiple CPUs does not mean that you don't do any time-division
multiplexing anymore. In even just moderately concurrent systems you probably
have many more threads than CPUs. Call/cc is a great way of expressing
the capture of the control state of one of these CPUs so that the corresponding
thread can resume at this point later -- regardless of whether this will happen
on the same or another CPU that happens to be idle at the time.

--
-Matthias

Matthias Blume

unread,
Nov 1, 2001, 2:39:39 PM11/1/01
to
j...@itasoftware.com wrote:


There is no way to have reliable unwind-protection. Period.
(It is all too easy, for example, to let a suspended computation escape
the protected region and have it continue later. Just think lazy streams.
This has nothing to do with call/cc. The main problem is state, in particular
global state. And there is no general way to deal with it cleanly and
reliably. Dynamic-wind is just an ugly hack, nothing more.)

--
-Matthias

David Feuer

unread,
Nov 1, 2001, 2:56:25 PM11/1/01
to
Matthias Blume wrote:
<SNAP!>

>
> See above. To say it slightly differently: Because call/cc can be used in
> very undisciplined ways. All of the uses that you list or that one could
> come up with actually correspond to rather disciplined uses of call/cc.
> Implementations can be simplified by taking advantage of such discipline.
> But once call/cc has been exposed to the programmer, such simplification
> is no longer possible because discipline is not guaranteed anymore.

I personally like GLS: "Lambda the Ultimate Goto". There is no way to
enforce a particular discipline in a complete language. Should Scheme
have more specialized constructs? Perhaps. But if a programmer wants
call/cc and doesn't have it, he/she will probably go and write explicit
CPS, which is even less readable.

>
> > (BTW, I find it hard to believe that you really think you can anticipate
> > every possible use of call/cc)
>
> You don't have to believe it because I don't think that. But is it worth
> catering to "every possible use" of something most programmers have a hard
> time grasping? They have a hard time for a reason: call/cc really *is* a
> rather puzzling thing.

Hmm.... dynamic wind seems approximately 10 times as complicated... As
soon as I read through its description in the standard I thought to
myself "here is an incredibly ugly construct with no obvious purpose".
Should something like that be in Scheme?

Richard Cobbe

unread,
Nov 1, 2001, 3:18:34 PM11/1/01
to
David Rush <ku...@bellsouth.net> writes:

> Richard Cobbe <co...@airmail.net> writes:
>
> > But. MzScheme v103 predefines 38 such parameters, It would rapidly
> > get very unwieldy to keep passing
>
> But you *don't* actually use all 38 eveywhere, so you only ever need to
> pass a subset.

True. However, it would seem to me that the subset you need to pass to a
particular function f is the union of the subsets required by all of the
functions that f calls. In some situations, this subset could approach the
set of all 38 such parameters rather rapidly, I'd think.

(I have to admit here that I don't have all that much experience designing
and writing large programs in advanced languages, so it may well be the
case that the situation I describe isn't all that common.)

> > Here, at least, I think there's an argument for using dynamic-wind for
> > convenience and readability.
>
> Nolo contendre, actually. It's just that call/wc can be implemented
> interms of call/cc. This makes replacing call/cc with call/wc wrong.

From a purely aesthetic standpoint, I'll agree---since Scheme is, in so
many ways, about getting to the fundamentals of computing, replacing a more
powerful form with a less powerful one doesn't make much sense. However,
having both could cause some difficulties, though. (I'll use the term
`normal continuation' to refer to a continuation captured by call/cc, as
opposed to one captured by call/wc.)

For instance: I'm using a third-party library, and I pass a callback
function as an argument to one of the library functions. Within this
callback, I invoke a normal continuation that I grabbed back up in my code.
If this were to skip over any dynamic-winds within the library, the
program's state may well become inconsistent, leading to a rather subtle
error condition later. I suppose the library's documentation could state
that such callbacks may invoke only winding continuations, not normal
continuations, but this adds a fair amount of complexity to the API
documentation.

Basically, it boils down to this: the fact that R5RS scheme has only
winding continuations means that the author of the library above can use
dynamic-wind where necessary and be sure that the application which uses
the library can't circumvent it. This improves the modularity of the
design of the application and the library both.

Richard

Matthias Blume

unread,
Nov 1, 2001, 3:41:23 PM11/1/01
to
Richard Cobbe wrote:

> David Rush <ku...@bellsouth.net> writes:
>
>
>>Richard Cobbe <co...@airmail.net> writes:
>>
>>
>>>But. MzScheme v103 predefines 38 such parameters, It would rapidly
>>>get very unwieldy to keep passing
>>>
>>But you *don't* actually use all 38 eveywhere, so you only ever need to
>>pass a subset.
>>
>
> True. However, it would seem to me that the subset you need to pass to a
> particular function f is the union of the subsets required by all of the
> functions that f calls. In some situations, this subset could approach the
> set of all 38 such parameters rather rapidly, I'd think.


I would use a two-pronged approach:

Some of these 38 parameters are probably pretty fundamental. One example of
a "fundamental" parameter is the current exception handler. These parameters
should be built into the language proper as part of (in this case) the
exception mechanism. Call/cc would then be implemented so as to capture and
restore the current exception handler along with the current continuation.
(It should be plural: "call-with-current-continuationS", because the exception handler
is nothing more than an alternative continuation.)


> From a purely aesthetic standpoint, I'll agree---since Scheme is, in so
> many ways, about getting to the fundamentals of computing, replacing a more
> powerful form with a less powerful one doesn't make much sense. However,
> having both could cause some difficulties, though. (I'll use the term
> `normal continuation' to refer to a continuation captured by call/cc, as
> opposed to one captured by call/wc.)
>
> For instance: I'm using a third-party library, and I pass a callback
> function as an argument to one of the library functions. Within this
> callback, I invoke a normal continuation that I grabbed back up in my code.
> If this were to skip over any dynamic-winds within the library, the
> program's state may well become inconsistent, leading to a rather subtle
> error condition later. I suppose the library's documentation could state
> that such callbacks may invoke only winding continuations, not normal
> continuations, but this adds a fair amount of complexity to the API
> documentation.
>
> Basically, it boils down to this: the fact that R5RS scheme has only
> winding continuations means that the author of the library above can use
> dynamic-wind where necessary and be sure that the application which uses
> the library can't circumvent it. This improves the modularity of the
> design of the application and the library both.


In a language where you have both call/cc and call/wc (i.e., not R5RS Scheme),
the purpose of invoking a normal continuation is to jump somewhere AND THEN
COME BACK. In other words, the code that invokes such a continuation wants
to break out of the normal flow of control but then resume and act "as if it
had never been away". Cooperative or time-sliced multitasking is the prime
example for this situation. This would work well even with your third-party
library because the library will never know that control had been transferred
away.

However, if you invoke a continuation with the purpose of leaving and not
coming back, then you better use a winding continuation. Now things are rather
messy anyway -- winding continuations are not the solution to all problems.
To twist someone else's example a bit: Assume we want to simulate a "current
working directory" on a per-thread basis. Thus, a thread context switch will
probably want to do all the cwd-related winding. On the other hand, it would
probably _not_ want to do any winding related to open ports.

I guess one could put much more design effort into all of this, for example I
could imagine having first-class winding contexts of some sort that let you
select explicitly which context you want to be in. Whether such thing is worth
the effort is not clear. Personally, I lean in the direction of "not worth it".

--
-Matthias

Matthias Blume

unread,
Nov 1, 2001, 3:44:37 PM11/1/01
to
Matthias Blume wrote:

> Richard Cobbe wrote:

>> True. However, it would seem to me that the subset you need to pass to a
>> particular function f is the union of the subsets required by all of the
>> functions that f calls. In some situations, this subset could
>> approach the
>> set of all 38 such parameters rather rapidly, I'd think.
>
>
>
> I would use a two-pronged approach:
>

[ describes the first prong but not the second... ]


Oops, I never said what the other prong was:

For non-fundamental parameters you can bundle them up into a data structure
(aka "dictionary"). This way you can pass all of them around without
having to have multiple extra arguments.

--
-Matthias

Jeffrey Siegal

unread,
Nov 1, 2001, 4:19:00 PM11/1/01
to
Ray Dillinger wrote:

> Depending on the GC system, you can. I remember that in
> some schemes, objects that don't contain references to
> other objects (characters, strings, and more importantly
> file handles) are guaranteed to get collected instantly
> when the last pointer to them is dropped. It's actually
> pretty easy to refcount such objects fairly efficiently and
> do instant-collection.

This only works if the reference to the atomic object is explicitly
destroyed (using a set! type operator). I think the more common case is
that the reference exists as part of some other structure and only gets
destroyed when that other structure is collected. Also, using a set!
type operator to destroy a reference to something like a port is pretty
much the same thing as a close operator. The object containing the
destroyed reference still exists, but can no longer be used to access
the file.

j...@itasoftware.com

unread,
Nov 1, 2001, 4:24:24 PM11/1/01
to
Ray Dillinger <be...@sonic.net> writes:

> David Rush wrote:
> >
> > Methinks you are missing the point. The point is that it is a
> > *feature* of R4RS call/cc that it doesn't do all the winding magic. As
> > Matthias (and others) have shown call/wc (R5RS call/cc) can be
> > implemented entirely in terms of call/cc, but not vice-versa. This
> > makes call/cc *more fundamental* than call/wc, and therefore the bit
> > that is supposed to be in the standard. Building a language from
> > small, orthogonal concepts is a Good Thing (TM). All that winding
> > magic is just a control structure built out of the fundamental control
> > operator call/cc.
> >
> > Bring back R4RS call/cc!
> >
>
> This is the most compelling argument I've seen so far.
>

Ah, but you cannot have a working dynamic-wind (as in impossible to
subvert) with r4rs call/cc. The need for this is more compelling.

j...@itasoftware.com

unread,
Nov 1, 2001, 4:26:09 PM11/1/01
to
Ray Dillinger <be...@sonic.net> writes:

> But all the objects that require finalization are
> primitive objects and can be collected with optimized
> refcounts, which enables guarantees of timeliness.

This isn't true. I have often made higher level objects that are
implemented in terms of lower level ones that require finalization.

j...@itasoftware.com

unread,
Nov 1, 2001, 4:34:25 PM11/1/01
to
David Rush <ku...@bellsouth.net> writes:

> Sorry. I don't agree. Invoking a captured continuation is the same
> thing as invoking any other function.

No, when you invoke a function you get a return value back. When you
invoke a continuation you don't. It is nice that you do not need a
special form (e.g. throw) to invoke the continuation, but it is
clearly something different from calling a function.

> The fact that call/cc is often implemented by a stack capture
> doesn't change that fact. The stack-capturing implementations need
> to change; not the semantics of call/cc.

What are you talking about?

> > I used Baker's method of never returning. Call/cc took essentially no
> > time whatsoever.
>
> Which would definitely lead to your problems in a multi-processor
> environment.

Why? I had no difficulty with this.

> All of which goes to show that Baker's method of continuation
> capture is inadequate.

Inadequate for what? It works just fine. The biggest problem is
interaction with the processor cache.

> But what do you expect? call/cc is essentially a *syntactic*
> operation.

What are you smoking? The semantics for call/cc are clearly spelled
out in RnRS.

> Implementing it at run-time is only going to cause problems.

I know of no real implementation that does not implement call/cc as a
special primitive form.

j...@itasoftware.com

unread,
Nov 1, 2001, 4:36:08 PM11/1/01
to
Matthias Blume <bl...@research.bell-labs.com> writes:

> Over the years I simply have not seen *any* interesting uses of it
> that are not better captured by their respective separate language
> constructs.

It is quite useful for *implementing* the respective separate language
constructs.

j...@itasoftware.com

unread,
Nov 1, 2001, 4:42:07 PM11/1/01
to
Matthias Blume <bl...@research.bell-labs.com> writes:

> I completely disagree. A continuation is -- in concrete terms -- the state
> of control of a process/thread.

Yes, but the process/thread is a trajectory through state space.

> To me it makes perfect sense for one CPU to take such a state and
> re-instate it -- even if the state was captured on another CPU. At
> least in a memory-shared world this makes perfect sense. In fact,
> SMP operating systems do precisely that.

It makes sense provided you are not duplicating the thread! In other
words, it's fine to migrate threads around the hardware, but if
processor A invokes a continuation pending on processor B, then you
suddenly have two processors acting on a single `thread'.

> And having multiple CPUs does not mean that you don't do any time-division
> multiplexing anymore. In even just moderately concurrent systems you probably
> have many more threads than CPUs.

Yes, but the number of processors should be hidden below this level of
abstraction.

> Call/cc is a great way of expressing the capture of the control
> state of one of these CPUs so that the corresponding thread can
> resume at this point later -- regardless of whether this will happen
> on the same or another CPU that happens to be idle at the time.

I agree with this, actually, with one caveat: You have to retain the
identity of the thread.

j...@itasoftware.com

unread,
Nov 1, 2001, 4:45:28 PM11/1/01
to
Matthias Blume <bl...@research.bell-labs.com> writes:

> There is no way to have reliable unwind-protection. Period.
> (It is all too easy, for example, to let a suspended computation escape
> the protected region and have it continue later. Just think lazy streams.
> This has nothing to do with call/cc. The main problem is state, in particular
> global state. And there is no general way to deal with it cleanly and
> reliably. Dynamic-wind is just an ugly hack, nothing more.)

I disagree. Dynamic-wind is the logical extension of unwind-protect
and the analog of the myriad `try/finally' kind of clauses in other
languages. These have proven to be useful constructs.

Jeffrey Siegal

unread,
Nov 1, 2001, 4:46:35 PM11/1/01
to
j...@itasoftware.com wrote:

>>Sorry. I don't agree. Invoking a captured continuation is the same
>>thing as invoking any other function.
>>
>
> No, when you invoke a function you get a return value back.

Only if the function ever returns one, which not all functions do.

Thant Tessman

unread,
Nov 1, 2001, 4:49:33 PM11/1/01
to
j...@itasoftware.com wrote:

> David Rush <ku...@bellsouth.net> writes:
>
>
>>Sorry. I don't agree. Invoking a captured continuation is the same
>>thing as invoking any other function.
>>
>
> No, when you invoke a function you get a return value back. When you

> invoke a continuation you don't. [...]


Huh? Sure you do. Or at least you can. And when implementing coroutines
you have to.

[...]

-thant


Jeffrey Siegal

unread,
Nov 1, 2001, 4:50:58 PM11/1/01
to
j...@itasoftware.com wrote:

> Matthias Blume <bl...@research.bell-labs.com> writes:
>
>
>>I completely disagree. A continuation is -- in concrete terms -- the state
>>of control of a process/thread.
>>
>
> Yes, but the process/thread is a trajectory through state space.


Not in a multithreaded environment it isn't, since the state space
includes the global store, and the global store could be modified by
another thread. So the trajectory through state space is not defined by
an individual threads but by the execution of all of the threads.

> It makes sense provided you are not duplicating the thread! In other
> words, it's fine to migrate threads around the hardware, but if
> processor A invokes a continuation pending on processor B, then you
> suddenly have two processors acting on a single `thread'.

That really depends how you define "thread." There is more than one
reasonable way of doing so.


> I agree with this, actually, with one caveat: You have to retain the
> identity of the thread.

That's not essential for concurrant programming (a better name than
multithreaded, I think because the name multithreaded does suggest the
existance of a "thread" entity).

j...@itasoftware.com

unread,
Nov 1, 2001, 4:52:10 PM11/1/01
to
Thant Tessman <th...@acm.org> writes:

No, you don't. When you *capture* a continuation you get a value
back. When you invoke a continuation, the `current' continuation is
discarded in favor of the one you invoked and there is no way to get
at it if you haven't saved it.

j...@itasoftware.com

unread,
Nov 1, 2001, 4:56:38 PM11/1/01
to
Jeffrey Siegal <j...@quiotix.com> writes:

> j...@itasoftware.com wrote:
>
> > Matthias Blume <bl...@research.bell-labs.com> writes:
> >
>
> >>I completely disagree. A continuation is -- in concrete terms -- the state
> >> of control of a process/thread.
>
> > Yes, but the process/thread is a trajectory through state space.
>
> Not in a multithreaded environment it isn't, since the state space
> includes the global store, and the global store could be modified by
> another thread. So the trajectory through state space is not defined
> by an individual threads but by the execution of all of the threads.

This is superfluous detail. We factor the `state' of the machine into
the state in the store and the `control state'. When you capture a
continuation you have the opportunity to modify the global state
before invoking it. If we didn't factor the state, then any
modification to the store simultaneously side effects *all* extant
continuations (yuck).

> > It makes sense provided you are not duplicating the thread! In other
> > words, it's fine to migrate threads around the hardware, but if
> > processor A invokes a continuation pending on processor B, then you
> > suddenly have two processors acting on a single `thread'.
>
> That really depends how you define "thread." There is more than one
> reasonable way of doing so.

Such as?

> > I agree with this, actually, with one caveat: You have to retain the
> > identity of the thread.
>
> That's not essential for concurrant programming (a better name than
> multithreaded, I think because the name multithreaded does suggest the
> existance of a "thread" entity).

Again, this begs the question of what exactly is going on
concurrently. Whatever that is, I want to call it a thread.

Matthias Blume

unread,
Nov 1, 2001, 4:57:43 PM11/1/01
to
j...@itasoftware.com wrote:

Just what I said. But that does not mean that one has to give it to the
programmer. As the implementer, feel free to use it to your heart's
content!

As the implementer, you have much more control over the invariants that
you maintain in your code. Once you let the beast out, you don't have
this kind of control anymore -- resulting in potentially messy control
flow and the apparent need for kludgy hacks such as dynamic-wind (which
do not even solve the general problem but might fool some programmers into
thinking they do).

--
-Matthias

Matthias Blume

unread,
Nov 1, 2001, 4:54:18 PM11/1/01
to
j...@itasoftware.com wrote:

No, the need is not compelling at all. If I had to program in a language
that has dynamic-wind, then I definitely want to be able to "subvert" it
(to use your word)!

--
-Matthias

Matthias Blume

unread,
Nov 1, 2001, 5:08:48 PM11/1/01
to
Jeffrey Siegal wrote:

> j...@itasoftware.com wrote:
>
>> Matthias Blume <bl...@research.bell-labs.com> writes:
>>
>>
>>> I completely disagree. A continuation is -- in concrete terms -- the
>>> state
>>> of control of a process/thread.
>>
>>
>> Yes, but the process/thread is a trajectory through state space.
>
>
>
> Not in a multithreaded environment it isn't, since the state space
> includes the global store,


No, it does not include the store. Notice that I wrote "state OF CONTROL".
In terms of DS, you capture the kappa but not the sigma.


--
-Matthias

Matthias Blume

unread,
Nov 1, 2001, 5:05:12 PM11/1/01
to
j...@itasoftware.com wrote:

BUT THEY DON'T WORK RELIABLY IN THE PRESENCE OF HIGHER-ORDER FUNCTIONS!

--
-Matthias

Matthias Blume

unread,
Nov 1, 2001, 5:04:27 PM11/1/01
to
j...@itasoftware.com wrote:

> Matthias Blume <bl...@research.bell-labs.com> writes:
>
>
>>I completely disagree. A continuation is -- in concrete terms -- the state
>>of control of a process/thread.
>>
>
> Yes, but the process/thread is a trajectory through state space.


Yes. That's exactly how I think of threads. Threads are trajectories
in continuation space, and continuations are points on these trajectories.


>>To me it makes perfect sense for one CPU to take such a state and
>>re-instate it -- even if the state was captured on another CPU. At
>>least in a memory-shared world this makes perfect sense. In fact,
>>SMP operating systems do precisely that.
>>
>
> It makes sense provided you are not duplicating the thread!


Yes, making sure you invoke every continuation at most once makes many
things easier. That's precisely why I don't want to expose call/cc
to the programmer because once I do that I no longer have such a guarantee.

However, when viewed from the right angle, then even invoking the same
continuation a second time can make sense: What you have done then
is to spawn another thread.

> In other
> words, it's fine to migrate threads around the hardware, but if
> processor A invokes a continuation pending on processor B, then you
> suddenly have two processors acting on a single `thread'.


No, you have two processors acting on two threads which have a common past.

>>And having multiple CPUs does not mean that you don't do any time-division
>>multiplexing anymore. In even just moderately concurrent systems you probably
>>have many more threads than CPUs.
>>
>
> Yes, but the number of processors should be hidden below this level of
> abstraction.


Well, just what I said. *I* do not want to expose call/cc or # of processors
or such to the programmer at all. I would expose an interface such as, e.g.,
CML and be done with it. What's happening "under the hood" is the implementer's
worry, not the programmer's.


>>Call/cc is a great way of expressing the capture of the control
>>state of one of these CPUs so that the corresponding thread can
>>resume at this point later -- regardless of whether this will happen
>>on the same or another CPU that happens to be idle at the time.
>>
>
> I agree with this, actually, with one caveat: You have to retain the
> identity of the thread.


I am not sure about why a thread necessarily needs an "identity". But even if
you insist, I am sure this could be arranged somehow...

--
-Matthias

Ray Dillinger

unread,
Nov 1, 2001, 5:16:39 PM11/1/01
to

Argh, right, I see. If you had both in the language, regardless
of which were implemented in terms of the other, it would have to
be an error to capture one type of continuation inside the other.

R4RS continuations can't be permitted to capture R5RS continuations
because they can subvert the winding protections. R5RS continuations
can't be permitted to capture R4RS continuations because then winding
thunks will get called in contexts where they can cause errors.

Thus, capturing either kind of continuation *safely* would require
walking the entire memory arena to make sure no continuations of the
other type were in the state being captured. And that's even uglier
than doing all that winding stuff when you don't need it.

Bear

Jeffrey Siegal

unread,
Nov 1, 2001, 5:23:37 PM11/1/01
to
j...@itasoftware.com wrote:

>>>>I completely disagree. A continuation is -- in concrete terms -- the state
>>>>of control of a process/thread.
>>>>
>>>Yes, but the process/thread is a trajectory through state space.
>>>
>>Not in a multithreaded environment it isn't, since the state space
>>includes the global store, and the global store could be modified by
>>another thread. So the trajectory through state space is not defined
>>by an individual threads but by the execution of all of the threads.
>>
>
> This is superfluous detail. We factor the `state' of the machine into
> the state in the store and the `control state'.

In other words, a thread is a trajectory through *control* state space,
which is reasonable, but it isn't what you said.


>>That really depends how you define "thread." There is more than one
>>reasonable way of doing so.
>>
>
> Such as?


Such as defining it to be a trajectory through control state space.
Using that definition, a thread need not have any identity beyond the
conceptual, though points along that trajectory may have identity
(continuation).

Another definition of thread, which is the one you seem to be fond of,
makes it a concrete entity, with a defined identity, manipulation API
and so forth. In this model, a "thread" certainly follows a trajectory,
but points along the trajectory may not have identity (as in systems
without first class continuations).

Trying to give both entities (the path as well as points along the path)
full identity is problematic for precisely the reason you suggest, which
is that invoking a continuation from one thread on another thread has
semantics which are unclear and not obviously useful. But that doesn't
make one model more legitimate than the other.

Thant Tessman

unread,
Nov 1, 2001, 5:23:48 PM11/1/01
to
j...@itasoftware.com wrote:


In other words, the only way that invoking a continuation can give you a
value back is if you invoke it with call/cc--which you claim makes this
fundamentally different than a function call. Wouldn't this be a matter
of how the language was implemented?

-thant

Jeffrey Siegal

unread,
Nov 1, 2001, 5:25:11 PM11/1/01
to
Matthias Blume wrote:

You did but jrm dropped it. I completely agree with your wording.


Ji-Yong D. Chung

unread,
Oct 31, 2001, 8:08:25 PM10/31/01
to
Hi
"Matthias Blume" <bl...@research.bell-labs.com> wrote in message
news:3BE0692E...@research.bell-labs.com...
> Anyway, I think we beat this horse to death...

It was already a dead horse.

The arguments _for_ dynamic wind in Scheme as it stands
are pretty weak.


Ozan Yigit

unread,
Nov 1, 2001, 8:39:23 PM11/1/01
to
j...@itasoftware.com writes: [in part]

> Matthias Blume <bl...@research.bell-labs.com> writes:
>
> > To me it makes perfect sense for one CPU to take such a state and
> > re-instate it -- even if the state was captured on another CPU. At
> > least in a memory-shared world this makes perfect sense. In fact,
> > SMP operating systems do precisely that.
>
> It makes sense provided you are not duplicating the thread! In other
> words, it's fine to migrate threads around the hardware, but if
> processor A invokes a continuation pending on processor B, then you
> suddenly have two processors acting on a single `thread'.

again a look back at literature may be appropriate; does wonders
for bandwidth. before i abandoned all work in scheme some years ago,
i remember seeing some work by Marc Feeley on multi-processor virtual
machines for Scheme; if my memory serves, that may be an enlightening
read. [marc should chime in and say my memory is worse than
swiss cheeze at this point.. :)]

[alas, from the discussion, looks like gabriel's curse may have
gotten the best of schemers in R5RS...]

oz

Stephan H.M.J. Houben

unread,
Nov 2, 2001, 3:47:32 AM11/2/01
to
On Thu, 01 Nov 2001 22:16:39 GMT, Ray Dillinger <be...@sonic.net> wrote:

>Argh, right, I see. If you had both in the language, regardless
>of which were implemented in terms of the other, it would have to
>be an error to capture one type of continuation inside the other.

I think it is possible to combine the two, safely.

Consider the following model:

1. At every point during execution of the program, there is a
list of pending windings.
2. Every reified continuation (created with either call/cc or call/wc)
contains the pending windings of when it was created.
3. Whenever a reified continuation is invoked, it restores the
pending windings to their stored value.
4. *Only* when the continuation was created with call/wc, the
winding handlers are invoked in the same way as what now happens
with the R5RS call/cc.

This means that call/wc can be used for exception handling and the
like, and call/cc can be used for co-routines and cooperative
threading. And this will nicely work together in a program.

Stephan

David Rush

unread,
Nov 2, 2001, 4:53:17 AM11/2/01
to

Just like the original continuation, really then.

david rush
--
No matter how far down the wrong road you've gone, turn back.
-- Turkish proverb

David Rush

unread,
Nov 2, 2001, 5:04:31 AM11/2/01
to
j...@itasoftware.com writes:
> David Rush <ku...@bellsouth.net> writes:
> > Sorry. I don't agree. Invoking a captured continuation is the same
> > thing as invoking any other function.

<your comments snipped due to better answers from others>

> > The fact that call/cc is often implemented by a stack capture
> > doesn't change that fact. The stack-capturing implementations need
> > to change; not the semantics of call/cc.
>
> What are you talking about?

call/cc can be implemented very directly via conversion to CPS and
subsequent textual manipulation of the exposed continuations. This
idea was also exposed in the recent discussion on out-of-band return
values and (values ...). Once you get used to programming in CPS a lot
of this stuff becomes No Big Deal.

call/cc, IMO, needs to remain in the language for primarily
two reasons:

1) it is syntactically *much* cleaner than transforming large
portions of a program into CPS by hand.
2) You don't always have the engineering capability of
performing the transformation (e.g. dealing with
code-generator output)

Otherwise proper tail-calling in Scheme is all you really need.

> > > I used Baker's method of never returning. Call/cc took essentially no
> > > time whatsoever.
> >
> > Which would definitely lead to your problems in a multi-processor
> > environment.
>
> Why? I had no difficulty with this.

You complained that call/cc doesn't work correctly for multi-tasking
in a multiple CPU environment. I assumed that you had actually
attempted to use it in such a way. Sorry if I'm mistaken.

> > All of which goes to show that Baker's method of continuation
> > capture is inadequate.
>
> Inadequate for what?

Your context (multi-processor multi-tasking and continuation
capture). I've been chopping pretty heavily lately. My apologies.

> > But what do you expect? call/cc is essentially a *syntactic*
> > operation.
>
> What are you smoking? The semantics for call/cc are clearly spelled
> out in RnRS.

Nothing. R4RS call/cc can be implemented by a straightforward
application of the CPS-transform. This is a syntactic operation, no?

> > Implementing it at run-time is only going to cause problems.
>
> I know of no real implementation that does not implement call/cc as a
> special primitive form.

Well, that's because people think that call/cc and let/cc are
equivalent. I think that call/cc leads to confused thinking about the
nature of the operation. Anyway, Unless I misunderstand Stalin's
documentation and messages, it implements call/cc as a syntactic
transform. I could be wrong on this, I haven't read the code too
closely.

david rush
--
I repeat myself when under stress. I repeat myself when under
stress. I repeat myself when under stress. I repeat myself when
under stress. I repeat myself when under stress. I repeat myself
when under stress. I repeat myself when under stress. I repeat

felix

unread,
Nov 2, 2001, 6:50:44 AM11/2/01
to

Matthias Blume wrote in message <3BE1A00E...@research.bell-labs.com>...
>
>See above. To say it slightly differently: Because call/cc can be used in
>very undisciplined ways. All of the uses that you list or that one could
>come up with actually correspond to rather disciplined uses of call/cc.
>Implementations can be simplified by taking advantage of such discipline.
>But once call/cc has been exposed to the programmer, such simplification
>is no longer possible because discipline is not guaranteed anymore.
>
>[...]

>
>Over the years I simply have not seen *any* interesting uses of it that are
>not better captured by their respective separate language constructs. The only
>other use of call/cc is to make mind-bending puzzles such as
>((call/cc call/cc) (call/cc call/cc)) etc. I don't think that being able to
>do *that* is worth the trouble.
>


I always considered Scheme a low-level language. I think call/cc fits
very nicely into Scheme's concept of a being very simple, very orthogonal and very
powerful. Because Scheme has a macro system, all
the syntactic sugar other languages need (exceptions, etc.) is
not necessary.

Also, you can not guard the programmer from using language
constructs in undisciplined ways (and you shouldn't). Lack of
dicipline is the programmers fault.


cheers,
felix


j...@itasoftware.com

unread,
Nov 2, 2001, 9:53:43 AM11/2/01
to
Matthias Blume <bl...@research.bell-labs.com> writes:

> As the implementer, you have much more control over the invariants that
> you maintain in your code. Once you let the beast out, you don't have
> this kind of control anymore -- resulting in potentially messy control
> flow and the apparent need for kludgy hacks such as dynamic-wind (which
> do not even solve the general problem but might fool some programmers into
> thinking they do).

Fooled me, that's for sure. I still think dynamic-wind is essential.

j...@itasoftware.com

unread,
Nov 2, 2001, 9:59:36 AM11/2/01
to
ste...@pcrm.win.tue.nl (Stephan H.M.J. Houben) writes:

> On Thu, 01 Nov 2001 22:16:39 GMT, Ray Dillinger <be...@sonic.net> wrote:
>
> >Argh, right, I see. If you had both in the language, regardless
> >of which were implemented in terms of the other, it would have to
> >be an error to capture one type of continuation inside the other.
>
> I think it is possible to combine the two, safely.
>
> Consider the following model:
>
> 1. At every point during execution of the program, there is a
> list of pending windings.
> 2. Every reified continuation (created with either call/cc or call/wc)
> contains the pending windings of when it was created.
> 3. Whenever a reified continuation is invoked, it restores the
> pending windings to their stored value.
> 4. *Only* when the continuation was created with call/wc, the
> winding handlers are invoked in the same way as what now happens
> with the R5RS call/cc.

You need one more clause, and this is the tricky one.

5. When invoking a continuation without running the unwinds, you
must guarantee that you will come back. (Modulo truly
exceptional situations, like abandoning a thread without
cleanup.) Otherwise, you have defeated the dynamic-winds.

I can think of no programmatic way of doing this other than relying on
the discipline of the user, so again, this destroys the semantics of
dynamic-wind.

Richard Cobbe

unread,
Nov 2, 2001, 8:17:38 AM11/2/01
to
Matthias Blume <bl...@research.bell-labs.com> writes:

> Richard Cobbe wrote:
>
> > David Rush <ku...@bellsouth.net> writes:
> >

> >>Richard Cobbe <co...@airmail.net> writes:
> >>
> >>>But. MzScheme v103 predefines 38 such parameters, It would rapidly
> >>>get very unwieldy to keep passing
> >>
> >>But you *don't* actually use all 38 eveywhere, so you only ever need to
> >>pass a subset.
> >
> > True. However, it would seem to me that the subset you need to pass to a
> > particular function f is the union of the subsets required by all of the
> > functions that f calls. In some situations, this subset could approach the
> > set of all 38 such parameters rather rapidly, I'd think.
>
> I would use a two-pronged approach:
>
> Some of these 38 parameters are probably pretty fundamental. One example
> of a "fundamental" parameter is the current exception handler. These
> parameters should be built into the language proper as part of (in this
> case) the exception mechanism. Call/cc would then be implemented so as
> to capture and restore the current exception handler along with the
> current continuation. (It should be plural:
> "call-with-current-continuationS", because the exception handler is
> nothing more than an alternative continuation.)

So, if I understand you correctly, the primary advantage of this is that
the application programmer doesn't have to worry about saving and restoring
the current exception handler whenever he invokes a continuation, yes?

I'm not sure I see how this avoids your primary complaint about
dynamic-wind, though: given the presence of first-class functions, it's
still possible to have a suspended computation (as in streams) `escape'
into a context with a different exception handler.

Or am I missing something?

Richard

j...@itasoftware.com

unread,
Nov 2, 2001, 10:10:48 AM11/2/01
to
Thant Tessman <th...@acm.org> writes:

The end result is fundamentally different. When invoking apply, you
have a current continuation. If you are applying a primitive, you
will invoke that continuation on the result. If you are applying a
closure, you will invoke the body of the closure with that
continuation. If you are applying a continuation, you will discard
the continuation passed to APPLY (unless you captured it elsewhere).

j...@itasoftware.com

unread,
Nov 2, 2001, 10:22:23 AM11/2/01
to
David Rush <ku...@bellsouth.net> writes:

> j...@itasoftware.com writes:
> > David Rush <ku...@bellsouth.net> writes:
> > > Sorry. I don't agree. Invoking a captured continuation is the same
> > > thing as invoking any other function.
>
> <your comments snipped due to better answers from others>
>
> > > The fact that call/cc is often implemented by a stack capture
> > > doesn't change that fact. The stack-capturing implementations need
> > > to change; not the semantics of call/cc.
> >
> > What are you talking about?
>
> call/cc can be implemented very directly via conversion to CPS and
> subsequent textual manipulation of the exposed continuations. This
> idea was also exposed in the recent discussion on out-of-band return
> values and (values ...). Once you get used to programming in CPS a lot
> of this stuff becomes No Big Deal.

Call-with-current-continuation reifies the current continuation. If
you CPS convert, you reify *all* the continuations, so
call-with-current-continuation simply becomes a use of the reified
continuation in an argument position. However, you generally *don't*
get to do this in real life. Most code is not written in CPS,
and interacting with a driver loop in CPS is painful.

> > > > I used Baker's method of never returning. Call/cc took essentially no
> > > > time whatsoever.
> > >
> > > Which would definitely lead to your problems in a multi-processor
> > > environment.
> >
> > Why? I had no difficulty with this.
>
> You complained that call/cc doesn't work correctly for multi-tasking
> in a multiple CPU environment. I assumed that you had actually
> attempted to use it in such a way. Sorry if I'm mistaken.

You can't expose the continuation used for time-sliced multitasking to
the user. If he invokes one, he'll seriously confuse the scheduler.
The `solution' is to have two basic types of continuations, a `process
state' that is manipulated *only* through the scheduler and process
manipulation API, and the kind returned by
call-with-current-continuation.

> > > All of which goes to show that Baker's method of continuation
> > > capture is inadequate.
> >
> > Inadequate for what?
>
> Your context (multi-processor multi-tasking and continuation
> capture). I've been chopping pretty heavily lately. My apologies.

It is adequate, you just have to be careful to hide the continuations
that are used for multitasking from the user.

> > > But what do you expect? call/cc is essentially a *syntactic*
> > > operation.
> >
> > What are you smoking? The semantics for call/cc are clearly spelled
> > out in RnRS.
>
> Nothing. R4RS call/cc can be implemented by a straightforward
> application of the CPS-transform. This is a syntactic operation,
> no?

Not in an environment where there is non CPS code. You'll find a
primitive call-with-current-continuation at the boundary where you
switch from `normal' to `CPS'.

Stephan H.M.J. Houben

unread,
Nov 2, 2001, 10:52:43 AM11/2/01
to

But actually, that guarantee doesn't exist now.
If the program never terminates, then the winder is obviously
never called. If the program exits with an error, then it is
undefined it the winder is called. I just checked and MzScheme
appears to *not* call the winders in this case.

What about a guarantee that any pending winders will be called
at program exit, whether the program exits normally or
by error. That's a stronger guarentee than we have now.

Stephan

j...@itasoftware.com

unread,
Nov 2, 2001, 11:10:55 AM11/2/01
to

Just because dynamic-wind can't solve the halting problem is no reason
to discard it for the useful cases.

Matthias Blume

unread,
Nov 2, 2001, 11:24:11 AM11/2/01
to
j...@itasoftware.com wrote:

You don't have to go to such lengths to "destroy" the (already broken) semantics
of dynamic-wind because you can escape any dynamic context simply by passing
higher-order values around. Even if you have no call/cc at all (winding or not),
dynamic-winds can be defeated easily.

--
-Matthias

Matthias Blume

unread,
Nov 2, 2001, 11:28:42 AM11/2/01
to
j...@itasoftware.com wrote:


True. If there just were any... :-)

--
-Matthias

Matthias Blume

unread,
Nov 2, 2001, 11:26:41 AM11/2/01
to
Richard Cobbe wrote:


No. This is a fundamental problem with any form of "dynamic context". Exception
handling is one of them.


--
-Matthias

Matthias Blume

unread,
Nov 2, 2001, 11:20:12 AM11/2/01
to
felix wrote:


> Also, you can not guard the programmer from using language
> constructs in undisciplined ways (and you shouldn't). Lack of
> dicipline is the programmers fault.

Ok, very well. If you are willing to pay the price: If you give
call/cc to the programmer, then you have to make it behave predictably
even if it is being used in messy ways. The cost of doing this is
completely wasted if programmers actually maintain discipline in their
code. And if they don't, well, then what you get is messy code.
Sounds like a lose-lose situation to me.

--
-Matthias

j...@itasoftware.com

unread,
Nov 2, 2001, 11:59:32 AM11/2/01
to
Matthias Blume <bl...@research.bell-labs.com> writes:

> You don't have to go to such lengths to "destroy" the (already broken) semantics
> of dynamic-wind because you can escape any dynamic context simply by passing
> higher-order values around. Even if you have no call/cc at all (winding or not),
> dynamic-winds can be defeated easily.

How, pray tell?

Consider this:

(define (foo receiver)
(display "start")

(receiver
(lambda (k)
(dynamic-wind (lambda () (display "enter"))
k
(lambda () (display "exit")))))

(display "end"))

Now I claim that no matter what value you concoct for receiver,
if both `start' and `end' are displayed, then between them there will
be zero or more pairs of `enter' `exit' displayed, and `enter' and
`exit' will alternate. (assume that the standard definitions of
DEFINE, DISPLAY, LAMBDA, and DYNAMIC-WIND are used.)

Matthias Blume

unread,
Nov 2, 2001, 12:23:16 PM11/2/01
to
j...@itasoftware.com wrote:

Maybe. (I'm too lazy to check.)

What's the point of the example? I don't see it.

Here is the challenge:

Suppose we have a global variable *foo*:

(define *foo* '())

Now define a procedure "with-foo" that takes a thunk and a value x and
guarantees that all code within the thunk will execute in a dynamic environment
where *foo* is set to the value of x (assuming there are no other assignments
to *foo*).

The "obvious" way would be:

(define (with-foo thunk x)
(let ((old-foo *foo*))
(dynamic-wind
(lambda () (set! *foo* x))
thunk
(lambda () (set! *foo* old-foo)))))

But this can easily be defeated by calling, e.g.,

(force (with-foo (lambda () (delay *foo*)) 'bar))

which will _not_ evaluate to "bar" as I asked.

Show me a version of "with-foo" that does what I am asking for (in the general case
and preferably without causing some huge performance bottleneck), and then we talk.

--
-Matthias

It is loading more messages.
0 new messages