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

Widespread bug in handling (call/cc (lambda (c) (0 (c 1)))) => 1

96 views
Skip to first unread message

Al Petrofsky

unread,
May 11, 2002, 5:38:42 PM5/11/02
to
The expression (call/cc (lambda (c) (0 (c 1)))) is a legal expression
according to r5rs, whose semantics are that 1 gets returned when it is
passed to the escape procedure c during the evaluation of the
positions of the combination (0 (c 1)).

A quick survey of the scheme implementations I have handy indicates
that this is a commonly missed compliance issue:

Petite Chez 6.0a Error: attempt to apply non-procedure
SCM 5d6 ERROR: Wrong type to apply
Bigloo 2.4b *** ERROR:bigloo:eval: Not a procedure
scsh 0.6.0 okay (gives a warning)
MzScheme 200alpha12 okay

Implementors, please place this on your list of earth-shatteringly
important issues, just after getting (let - ((n (- 1))) n) => -1
right.

-al

Jens Axel Søgaard

unread,
May 11, 2002, 6:24:21 PM5/11/02
to
Al Petrofsky wrote:
> The expression (call/cc (lambda (c) (0 (c 1)))) is a legal expression
> according to r5rs, whose semantics are that 1 gets returned when it is
> passed to the escape procedure c during the evaluation of the
> positions of the combination (0 (c 1)).

Subtle one. I was just about to complain, but the semantics clearly
says: "The operator and operand expressions are evaluated (in an
unspecified order) and " [then!] "the resulting procedure is passed the
resulting arguments.".

> A quick survey of the scheme implementations I have handy indicates
> that this is a commonly missed compliance issue:
>
> Petite Chez 6.0a Error: attempt to apply non-procedure
> SCM 5d6 ERROR: Wrong type to apply
> Bigloo 2.4b *** ERROR:bigloo:eval: Not a procedure
> scsh 0.6.0 okay (gives a warning)
> MzScheme 200alpha12 okay

My possibly old version (I use mzscheme) errors too:

guile> (call-with-current-continuation (lambda (c) (0 (c 1))))
standard input:2:45: In expression (0 (c 1)):
standard input:2:45: Wrong type to apply: (0 (c 1))
ABORT: (misc-error)
guile> (version)
"1.3.4"

--
Jens Axel Søgaard


Thomas Bushnell, BSG

unread,
May 11, 2002, 6:50:29 PM5/11/02
to
"Jens Axel Søgaard" <use...@soegaard.net> writes:

> Subtle one. I was just about to complain, but the semantics clearly
> says: "The operator and operand expressions are evaluated (in an
> unspecified order) and " [then!] "the resulting procedure is passed the
> resulting arguments.".

Yep! The error should be delayed until the procedure is actually
passed. You can *evaluate* the operator position, but you can't check
it for validity until later.

bri...@aracnet.com

unread,
May 11, 2002, 6:33:34 PM5/11/02
to

Interesting. Since the operator and operands are evaluated in an
unspecified order, why wouldn't "error - 0 is not a procedure" be a
legitimate result ?

Brian

>>>>> "Al" == Al Petrofsky <a...@petrofsky.org> writes:

Al> The expression (call/cc (lambda (c) (0 (c 1)))) is a legal
Al> expression according to r5rs, whose semantics are that 1 gets
Al> returned when it is passed to the escape procedure c during the
Al> evaluation of the positions of the combination (0 (c 1)).

Al> A quick survey of the scheme implementations I have handy
Al> indicates that this is a commonly missed compliance issue:

Al> Petite Chez 6.0a Error: attempt to apply non-procedure SCM 5d6
Al> ERROR: Wrong type to apply Bigloo 2.4b *** ERROR:bigloo:eval:
Al> Not a procedure scsh 0.6.0 okay (gives a warning) MzScheme
Al> 200alpha12 okay

Al> Implementors, please place this on your list of
Al> earth-shatteringly important issues, just after getting (let -
Al> ((n (- 1))) n) => -1 right.

Al> -al

--

"There is no right place for the dead to live."

-- Kai, last of the Brunnen-G

Matthias Radestock

unread,
May 11, 2002, 7:01:40 PM5/11/02
to
Al Petrofsky wrote:
> The expression (call/cc (lambda (c) (0 (c 1)))) is a legal expression
> ...

> Implementors, please place this on your list of earth-shatteringly
> important issues, just after getting (let - ((n (- 1))) n) => -1
> right.
>

I am pleased to report that SISC (http://sisc.sourceforge.net) handles
both correctly. It issues a compiler warning on the first but returns
the correct result.

I'm thinking of collecting an "implementation pitfalls" list for the
FAQ. It is a common misconception that Scheme is easy to implement - it
is indeed pretty easy to write an interpreter or even a compiler for a
scheme-like language, but implementing a fully R5RS-compliant Scheme is
far from straightforward.

From the top of my head, here's a list of pitfalls and things that are
easy to get wrong if not implemented with sufficient attention to detail:

* numeric overflows must not "wrap"

* redefinition of builtin functions/syntax must a) be possible, and b)
have no effect on any other builtin functions/syntax

* multi-shot continuations

* immutable literals and environments

* matching/expansion rules of patterns and templates in hygienic macros

* signalling an error in all the circumstances R5RS defines

* internal definitions, letrec, and their interaction with macro expansion


Some of the above fall into the "should have" rather than "must have"
category of the R5RS standard, but I believe it's still worthwhile
listing them.


Please add to the above list. Things you have found to be broken in at
least two Schemes probably make good candidates.


Matthias

Ben Goetter

unread,
May 11, 2002, 7:09:13 PM5/11/02
to
Quoth Al Petrofsky:

> The expression (call/cc (lambda (c) (0 (c 1)))) is a legal expression

Subtle. How on earth did you ever catch this? I must know.

ozan s yigit

unread,
May 11, 2002, 7:25:57 PM5/11/02
to
Al Petrofsky:

> The expression (call/cc (lambda (c) (0 (c 1)))) is a legal expression
> according to r5rs, whose semantics are that 1 gets returned when it is
> passed to the escape procedure c during the evaluation of the
> positions of the combination (0 (c 1)).

an old (r4rs) and never really distributed implementation:

$ ./psi


> (call/cc (lambda (c) (0 (c 1))))

1

heh.

oz
--
It seems so long and so many parentheses ago... -- thomas a. russ

Thomas Bushnell, BSG

unread,
May 11, 2002, 9:04:01 PM5/11/02
to
bri...@aracnet.com writes:

> Interesting. Since the operator and operands are evaluated in an
> unspecified order, why wouldn't "error - 0 is not a procedure" be a
> legitimate result ?

Because *evaluation* of the argument is not the same thing as
validating it to be a procedure. The validation step only happens at
apply time, which is specified to be after *all* the arguments have
been evaluated.

So yes, the system is free to evaluate "0" first, but it must evaluate
all the other arguments before attempting the procedure application.

Brian Campbell

unread,
May 11, 2002, 9:06:47 PM5/11/02
to
In article <abk8ap$319$0...@216.39.136.5>,
Ben Goetter <goe...@mazama.net.xyz> wrote:

Hav you not noticed the tendency of Al.* Petrofsky of torturing scheme
in every possible way to find every possible loophole in the language
and specification? He takes some sort of pleasure in discovering what
kinds of bizzare constucts he can come up with which are legal scheme
but never intended by any of the people designing the features or
writing the specs. If I remember correctly, he was the guy who
implemented set! with just macros, call/cc, and letrec. Yeah, here it is:

http://groups.google.com/groups?q=al+petrofsky+call/cc+set!&hl=en&safe=of
f&selm=87u1vrce18.fsf%40radish.petrofsky.org&rnum=1

Although actually, if you think about the semantics of call/cc and
function application, it would be fairly obvious that this would be
legal even without having a twisted mind like Al's.*

* p.s. Please not that I am not trying to insult Al by saying he has a
twisted mind. It's more of a compliment of sorts.

--
Brian Campbell
Real email: lambda (at) cs (dot) dartmouth (dot) edu

bri...@aracnet.com

unread,
May 12, 2002, 12:15:40 PM5/12/02
to
>>>>> "Thomas" == Thomas Bushnell, BSG <tb> writes:

Thomas> bri...@aracnet.com writes:
>> Interesting. Since the operator and operands are evaluated in an
>> unspecified order, why wouldn't "error - 0 is not a procedure" be
>> a legitimate result ?

Thomas> Because *evaluation* of the argument is not the same thing
Thomas> as validating it to be a procedure. The validation step
Thomas> only happens at apply time, which is specified to be after
Thomas> *all* the arguments have been evaluated.

Specified by you personally ? I was unable to find this in R5RS. Not
that I disagree with you, but this thread sort of started as a
nit-pick of r5rs compliancy.

Brian

Al Petrofsky

unread,
May 12, 2002, 3:38:16 PM5/12/02
to
bri...@aracnet.com writes:

> >>>>> "Thomas" == Thomas Bushnell, BSG <tb> writes:
>
> Thomas> Because *evaluation* of the argument is not the same thing
> Thomas> as validating it to be a procedure. The validation step
> Thomas> only happens at apply time, which is specified to be after
> Thomas> *all* the arguments have been evaluated.
>
> Specified by you personally ? I was unable to find this in R5RS. Not
> that I disagree with you, but this thread sort of started as a
> nit-pick of r5rs compliancy.

I admit that the r5rs informal semantics for combinations in section
4.1.3 are not 100% clear on this. The strange thing is that even the
expression (0) is not specified to be an error in 4.1.3. Nor is its
behavior explicitly unspecified. It is simply unmentioned. In
contrast, (apply 0 '()) is clearly specified to be a domain violation
error.

In the lack of any guidance to the contrary, an implementation is
certainly free to do whatever it wants when a non-procedure is invoked
as a procedure. The key word in that sentence is "when". Because
procedure invocation does not (and indeed, cannot) occur until after
all positions are evaluated, an implementation's freedom to be helpful
and signal an error does not arise until that time, and if that time
never comes (because one of the position evaluations escapes) then it
is noncompliant for the system to complain.

In the formal semantics in section 7.2, one can see that it is only if
all position evaluations complete that applicate is called, which in
turn may call wrong with the argument "bad procedure".

-al

P.S. I am not a language lawyer. I just play one on usenet.

Shriram Krishnamurthi

unread,
May 12, 2002, 5:21:52 PM5/12/02
to
Brian Campbell <lambd...@yahoo.com> writes:

> If I remember correctly, he was the guy who
> implemented set! with just macros, call/cc, and letrec.

Not to detract from Al.*'s enjoyable posts, but just to put it into
historical context, this problem is much older -- I believe it was
first posted on c.l.s by Alan Bawden in the late 80s, back when people
actually cared about the semantics of continuations. This was part of
a broader discussion about whether continuations were, to use Jon
Riecke and Albert Meyer's characterization, "unreasonable".

Someone will ask, so:

Albert R. Meyer and Jon G. Riecke. Continuations may be
unreasonable. In Proceedings of the 1988 ACM Conference on Lisp and
Functional Programming, pages 63--71.

Shriram

--
Shriram Krishnamurthi
Assistant Professor of Computer Science
Brown University

Alfresco Petrofsky

unread,
May 12, 2002, 7:32:09 PM5/12/02
to
sk...@cs.brown.edu (Shriram Krishnamurthi) writes:

> Brian Campbell <lambd...@yahoo.com> writes:
>
> > If I remember correctly, he was the guy who
> > implemented set! with just macros, call/cc, and letrec.
>
> Not to detract from Al.*'s enjoyable posts, but just to put it into
> historical context, this problem is much older -- I believe it was
> first posted on c.l.s by Alan Bawden in the late 80s, back when people
> actually cared about the semantics of continuations. This was part of
> a broader discussion about whether continuations were, to use Jon
> Riecke and Albert Meyer's characterization, "unreasonable".

I think you mean "back when people cared to talk about" continuations,
because there were still lots of new things to say about them. It
seems that people do still care about them now. In particular, people
who find them unreasonable still cite them as a reason that they
eschew scheme.


As for the MacGyver feat of implementing set! with just macros,
call/cc, letrec, a matchbox, a wad of gum, and a bucket of ball
bearings, I must point out that my novel contribution was that I
provided full-featured real scheme variables with assignment, rather
than just mutable objects as Bawden had. And I did properly credit
Bawden in that post, citing the full text of his February 1988 message
(which consisted of just a single "!" as the subject line, and 17
lines of code as the body). I even included the old message-id,
<323426.8...@AI.AI.MIT.EDU>, in the references header of my
article, for the convenience of those using a threaded reader with a
very large spool.

-al

William D Clinger

unread,
May 13, 2002, 12:17:01 AM5/13/02
to
Al Petrofsky wrote:
> The expression (call/cc (lambda (c) (0 (c 1)))) is a legal expression
> according to r5rs, whose semantics are that 1 gets returned when it is
> passed to the escape procedure c during the evaluation of the
> positions of the combination (0 (c 1)).

Not so. Twobit and Larceny's behavior is correct:

> (call/cc (lambda (c) (0 (c 1))))

Error: Reference to undefined global variable "call/cc".

If we're going to test for R5RS semantics, let's do it right:

> (call-with-current-continuation (lambda (c) (0 (c 1))))
1

> Implementors, please place this on your list of earth-shatteringly
> important issues, just after getting (let - ((n (- 1))) n) => -1
> right.

(Twobit and Larceny get that one right also. If this keeps up, the
non-bug list might grow longer than the bug list. :)

Will

Alex Shinn

unread,
May 13, 2002, 1:57:04 AM5/13/02
to
>>>>> "Al" == Al Petrofsky <a...@petrofsky.org> writes:

Al> A quick survey of the scheme implementations I have handy
Al> indicates that this is a commonly missed compliance issue:

For those keeping score, mit-scheme, stalin, chicken and gauche all get
both of these right.

--
Alex

Bruce Hoult

unread,
May 13, 2002, 4:31:40 AM5/13/02
to
In article <87g00y4...@radish.petrofsky.org>,
Al Petrofsky <a...@petrofsky.org> wrote:

> The expression (call/cc (lambda (c) (0 (c 1)))) is a legal expression
> according to r5rs, whose semantics are that 1 gets returned when it is
> passed to the escape procedure c during the evaluation of the
> positions of the combination (0 (c 1)).

Heck, Gwydion Dylan gets this right, and it's not even a Scheme :-)

begin
format-out("%=\n",
block (c)
0(c(1));
end)
end

=> 1

in fact, the compiler optimizes it to format-out("%=\n", 1).

-- Bruce

Scott G. Miller

unread,
May 13, 2002, 12:53:55 PM5/13/02
to
Al Petrofsky wrote:
> The expression (call/cc (lambda (c) (0 (c 1)))) is a legal expression
> according to r5rs, whose semantics are that 1 gets returned when it is
> passed to the escape procedure c during the evaluation of the
> positions of the combination (0 (c 1)).
>
> A quick survey of the scheme implementations I have handy indicates
> that this is a commonly missed compliance issue:
>
> Petite Chez 6.0a Error: attempt to apply non-procedure
> SCM 5d6 ERROR: Wrong type to apply
> Bigloo 2.4b *** ERROR:bigloo:eval: Not a procedure
> scsh 0.6.0 okay (gives a warning)
> MzScheme 200alpha12 okay
>
SISC issues a warning at compile time, but evaluates it correctly.

Bruce Lewis

unread,
May 13, 2002, 3:31:07 PM5/13/02
to
Bruce Hoult <br...@hoult.org> writes:

> Heck, Gwydion Dylan gets this right, and it's not even a Scheme :-)

Sure, but I bet Haskell implementors got it wrong. They probably lazily
evaluated functions in R5RS 7.2.4, making them think they could compute
applicate (epsilon* down 1) (epsilon* cross 1) kappa without having
computed all the epsilons yet.

(No offense to Haskell; this is just a joke that hopefully 1 or 2 people
will find funny.)

--
<brlewis@[(if (brl-related? message) ; Bruce R. Lewis
"users.sourceforge.net" ; http://brl.sourceforge.net/
"alum.mit.edu")]>

Al Petrofsky

unread,
May 13, 2002, 6:39:42 PM5/13/02
to
Matthias Radestock <matt...@sorted.org> writes:

> I'm thinking of collecting an "implementation pitfalls" list for the
> FAQ.

For things like (call/cc (lambda (c) (0 (c 1)))), the best form for
such a list might be an annotated test suite.

The link from your faq to an "r5rstest.scm" appears to be broken, but
google found me a file by that name, which is mostly the same as
Jaffer's r4rstest.scm from SCM, with some additions by Peter Norvig.

I found it interesting that the file attempts to test proper tail
recursion with tests like this:

(let go ((i n-tail-calls)) (if (= i 0) 0 (apply go (list (- i 1)))))

where n-tail-calls is a parameter you specify, which you should choose
to be sufficient to blow things up if a linear amount of space is
being used. It has a test for each of the tail contexts specified in
r5rs 3.5. The one for eval is broken, though:

(define (loop-eval i)
(if (= i 0) 0 (eval `(loop-eval ,(- i 1))
(scheme-report-environment 5))))
(loop-eval n-tail-calls)

This is an error because the scheme-report-environment should not have
the loop-eval binding. Writing a correct test is a fun exercise, a
variation on the problem of writing a program whose execution prints
its source code. Here's what I came up with:

(let ((code '(lambda (n code)
(or (zero? n)
(eval (list code (- n 1) (list 'quote code))
(scheme-report-environment 5))))))
(eval (list code n-tail-calls (list 'quote code))
(scheme-report-environment 5)))

Of course, such tests cannot be relied upon to *really* prove
anything.

> From the top of my head, here's a list of pitfalls and things that
> are easy to get wrong if not implemented with sufficient attention to
> detail:

> * redefinition of builtin functions/syntax must a) be possible, and b)


> have no effect on any other builtin functions/syntax

This is required for the standard variable bindings, and it is
certainly a good idea to do it for the standard syntax bindings too,
but it is not required.

If it were required, then enhancements to the binding mechanisms would
also have to be made (which would also be a good idea), or else the
library syntaxes could no longer be classified as "library",
i.e. "easily implemented in terms of the other, primitive, features",
because the standard macro system provides no syntax equivalent to the
idiom:

(define zero? (let ((= =)) (lambda (z) (= z 0))))

There are four issues here:

-- protecting a procedure from changes to variable bindings it uses.
-- protecting a procedure from changes to syntax bindings it uses.
-- protecting a transformer from changes to variable bindings it uses.
-- protecting a transformer from changes to syntax bindings it uses.

Only the first one is provided for in r5rs. The second is commonly
assumed to be automatic, but that is not guaranteed, and it is not
dome in systems like scm that do lazy macro-expansion (i.e., that
don't expand the body of a lambda until the first time a procedure
created by the lambda is applied):

(define-syntax m (syntax-rules () ((m) 1)))
(define (f) (m))
(define (g) (m))
(m) => 1
(f) => 1
(define-syntax m (syntax-rules () ((m) 2)))
(m) => 2
(f) => 1
(g) => 2
(define-syntax m (syntax-rules () ((m) 3)))
(m) => 3
(f) => 1
(g) => 2

-al

bri...@aracnet.com

unread,
May 13, 2002, 11:11:37 PM5/13/02
to
As long as we're at it :

For Gambit-C

> (define call/cc call-with-current-continuation)


> (call/cc (lambda (c) (0 (c 1))))

1


> (let - ((n (- 1))) n)

-1

felix

unread,
May 14, 2002, 5:56:43 AM5/14/02
to

Al Petrofsky wrote in message <87wuu73...@radish.petrofsky.org>...

>
>The link from your faq to an "r5rstest.scm" appears to be broken, but
>google found me a file by that name, which is mostly the same as
>Jaffer's r4rstest.scm from SCM, with some additions by Peter Norvig.


Where did you find that? The version Google gives me is unfortunately full
of bugs. Actually it looks like it has *never* been tested at all!
(unmatched parens, wrongly named variables, etc...)

Would you mind giving an URL, or even send/post the file?

>
>I found it interesting that the file attempts to test proper tail
>recursion with tests like this:
>
> (let go ((i n-tail-calls)) (if (= i 0) 0 (apply go (list (- i 1)))))
>
>where n-tail-calls is a parameter you specify, which you should choose
>to be sufficient to blow things up if a linear amount of space is
>being used.

Another test for proper TCO might be:

(letrec ((chain
(let loop ((i NNN))
(if (zero? i)
(lambda () (chain))
(lambda () ((loop (- i 1)))) ) ) ) )
(chain) )

With some NNN (say 100 - or even much lower, depending how good
the compiler is). Run this for a couple of hours and see what happens...


cheers,
felix


felix

unread,
May 14, 2002, 12:02:00 PM5/14/02
to

felix wrote in message <3ce0e6c5$0$350$9b62...@news.freenet.de>...

>
>Another test for proper TCO might be:
>
>(letrec ((chain
> (let loop ((i NNN))
> (if (zero? i)
> (lambda () (chain))
> (lambda () ((loop (- i 1)))) ) ) ) )
> (chain) )
>
>With some NNN (say 100 - or even much lower, depending how good
>the compiler is). Run this for a couple of hours and see what happens...
>


Actually this can be reduced to

(letrec ((chain
(let loop ((f #f))
(if f
(lambda () (chain))
(lambda () ((loop #t))) ) ) ) )
(chain) )

I just tested this with Stalin, though.


cheers,
felix


Matthias Blume

unread,
May 14, 2002, 12:31:56 PM5/14/02
to
Al Petrofsky <a...@petrofsky.org> writes:

> The expression (call/cc (lambda (c) (0 (c 1)))) is a legal expression
> according to r5rs, whose semantics are that 1 gets returned when it is
> passed to the escape procedure c during the evaluation of the
> positions of the combination (0 (c 1)).

For the record (not that I expect anyone to care at this point), VSCM
gets this "wrong" (in some sense):

D> (call-with-current-continuation (lambda (c) (0 (c 1))))
Top: call to non-procedure 0 in: (0 (c 1))
error: syntax error
; compile: 0.000 execute: 0.000 TOTAL: 0.000
!* Syntax error
D>

However, notice that this is a *compile-time* error -- the same one you
get when you type, e.g.:

(define (f x) (0 x))

or some such. I personally consider this a feature: The call to 0 can
never succeed, so there is no need for a program to have it in the
first place. By the way, since the compiler is extremely stupid, you
can easily defeat this very superficial analysis by putting the 0 one
step away:

(define (f x) (let ((zero 0)) (zero x)))

compiles fine. A more comprehensive solution is to use soft type
analysis.

In any case, I do not think that it is a big deal if a compiler gets
this facet "wrong" the way VSCM does.

> Implementors, please place this on your list of earth-shatteringly
> important issues, just after getting (let - ((n (- 1))) n) => -1
> right.

This is more serious, and I am embarrassed to admit that VSCM gets
this one wrong, too. Here is the fix, if anyone cares:

Index: pmac.scm
===================================================================
RCS file: Repositories/VScm/compiler/pmac.scm,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- pmac.scm 8 Nov 1994 17:22:55 -0000 1.8
+++ pmac.scm 14 May 2002 16:17:40 -0000 1.9
@@ -35,9 +35,10 @@
(check-let/let*-bindings bl complain)
(let ((vl (map car bl))
(el (map cadr bl)))
- `(,my-letrec
- ((,n (,my-lambda ,vl ,@body)))
- (,n ,@el)))))
+ `((,my-letrec
+ ((,n (,my-lambda ,vl ,@body)))
+ ,n)
+ ,@el))))
(begin
(check-let/let*-bindings first complain)
(let* ((vl (map car first))


I expect all implementations that get this wrong to be broken along
the same lines, and a fix for them should look similar to the above.

--
-Matthias

Thomas Bushnell, BSG

unread,
May 14, 2002, 1:11:29 PM5/14/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> In any case, I do not think that it is a big deal if a compiler gets
> this facet "wrong" the way VSCM does.

What? You think it's perfectly peachy if a compiler generates
incorrect code for valid input?

Al Petrofsky

unread,
May 14, 2002, 1:19:11 PM5/14/02
to
"felix" <felixu...@freenet.de> writes:

> Al Petrofsky wrote in message <87wuu73...@radish.petrofsky.org>...
> >
> >The link from your faq to an "r5rstest.scm" appears to be broken, but
> >google found me a file by that name, which is mostly the same as
> >Jaffer's r4rstest.scm from SCM, with some additions by Peter Norvig.
>
>
> Where did you find that? The version Google gives me is unfortunately full
> of bugs. Actually it looks like it has *never* been tested at all!
> (unmatched parens, wrongly named variables, etc...)

Uh, now that you mention it, yes, that describes the one I found too.
It was just sitting in a random directory somewhere and was not
advertised to be usable.

-al

Barry Margolin

unread,
May 14, 2002, 1:21:54 PM5/14/02
to
In article <87ptzy7...@becket.becket.net>,

What? You think it's perfectly peachy if people write nonsensical code
just because a loophole in the language specification makes it valid?

--
Barry Margolin, bar...@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.

Matthias Blume

unread,
May 14, 2002, 1:36:24 PM5/14/02
to

No, I think it is perfectly peachy if a compiler does not generate any
code at all for programs that are "obviously wrong" (for certain
values of "obviously wwrong" :-). Having a function call with
something in operator position that is statically known to never be a
function is "obviously wrong" since either the program never tries to
perform the function call (in which case it does not need to be there
in the first place) or it is certain to run afoul a runtime error.

--
-Matthias

Al Petrofsky

unread,
May 14, 2002, 2:02:45 PM5/14/02
to
Barry Margolin <bar...@genuity.net> writes:
> Thomas Bushnell, BSG <tb+u...@becket.net> wrote:
> >Matthias Blume <matt...@shimizu-blume.com> writes:
> >
> >> In any case, I do not think that it is a big deal if a compiler gets
> >> this facet "wrong" the way VSCM does.
> >
> >What? You think it's perfectly peachy if a compiler generates
> >incorrect code for valid input?
>
> What? You think it's perfectly peachy if people write nonsensical code
> just because a loophole in the language specification makes it valid?

What? You think it's perfectly peachy if people write a language
specification with a nonsensical construct like call/cc? But it's
unreasonable for people to use it as specified?

(I use "nonsensical" there in the same sense that you did: difficult,
but quite possible, to make sense of.)

Perhaps no human would write (0 (c 1)), but not all code is written by
humans, and I think it is helpful if scheme-targeting compilers (which
includes macros) can count on combinations being handled in the
consistent manner specified by r5rs.

-al

Barry Margolin

unread,
May 14, 2002, 2:28:06 PM5/14/02
to
In article <87lmam3...@radish.petrofsky.org>,

Al Petrofsky <a...@petrofsky.org> wrote:
>Barry Margolin <bar...@genuity.net> writes:
>> Thomas Bushnell, BSG <tb+u...@becket.net> wrote:
>> >Matthias Blume <matt...@shimizu-blume.com> writes:
>> >
>> >> In any case, I do not think that it is a big deal if a compiler gets
>> >> this facet "wrong" the way VSCM does.
>> >
>> >What? You think it's perfectly peachy if a compiler generates
>> >incorrect code for valid input?
>>
>> What? You think it's perfectly peachy if people write nonsensical code
>> just because a loophole in the language specification makes it valid?
>
>What? You think it's perfectly peachy if people write a language
>specification with a nonsensical construct like call/cc? But it's
>unreasonable for people to use it as specified?

call/cc isn't the nonsensical construct I was referring to.

>(I use "nonsensical" there in the same sense that you did: difficult,
>but quite possible, to make sense of.)

I can't make *any* sense of (0 (c 1)).

>Perhaps no human would write (0 (c 1)), but not all code is written by
>humans, and I think it is helpful if scheme-targeting compilers (which
>includes macros) can count on combinations being handled in the
>consistent manner specified by r5rs.

The 0 had to come from somewhere. And since it was put in the function
position of the resulting code, that place must have been intended to
contain a function. Whoever supplied the 0 provided a non-function where a
function was expected.

Maybe they knew that the function would never be called in this instance.
They still could have supplied a valid function; in fact, the best thing to
do would be to supply a function that reports that it shouldn't have been
called (in case of a bug that invalidated their a priori knowledge that the
function wouldn't be called). Supplying 0 as a value where a function is
needed is stupid.

That said, I would be happy if the compiler merely issued a warning but
managed to compile the code.

Matthias Blume

unread,
May 14, 2002, 3:20:16 PM5/14/02
to
Barry Margolin <bar...@genuity.net> writes:

This could trivially be arranged. I personally prefer not to do this
because my own coding ethics demand that I fix any warning from the
compiler anyway, so warnings might as well be errors.

--
-Matthias

Joe Marshall

unread,
May 14, 2002, 3:23:59 PM5/14/02
to

"Barry Margolin" <bar...@genuity.net> wrote in message news:SIbE8.14$Ij7...@paloalto-snr1.gtei.net...

> In article <87ptzy7...@becket.becket.net>,
> Thomas Bushnell, BSG <tb+u...@becket.net> wrote:
> >Matthias Blume <matt...@shimizu-blume.com> writes:
> >
> >> In any case, I do not think that it is a big deal if a compiler gets
> >> this facet "wrong" the way VSCM does.
> >
> >What? You think it's perfectly peachy if a compiler generates
> >incorrect code for valid input?
>
> What? You think it's perfectly peachy if people write nonsensical code
> just because a loophole in the language specification makes it valid?

I certainly think so.

It seems reasonable for the compiler to warn about something like
(0 (c 1)) because it is a very unusual form and will be an error
*if* c ever returns, but it shouldn't *assume* that c returns.
(If you are compiling with `low safety', perhaps the compiler should
assume that C *won't* return!)

Thomas Bushnell, BSG

unread,
May 14, 2002, 5:10:00 PM5/14/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

The point is that your disjunction fails in any language that allows
non-local exits, particularly given the possibility of automatically
generated code.

Thomas Bushnell, BSG

unread,
May 14, 2002, 5:07:52 PM5/14/02
to
Barry Margolin <bar...@genuity.net> writes:

> What? You think it's perfectly peachy if people write nonsensical code
> just because a loophole in the language specification makes it valid?

I don't know it's a loophole. I saw the example and immediately knew
the Right Thing was not to signal an error. Didn't even need to see
the exact R5RS text. Indeed, if the text didn't have that sentence, I
would have sent off a query to some RnRS editors I know and ask them
about adding it in the next version--so sure am I that the mandated
behavior is in fact the Right Thing.

While that particular case is indeed nonsensical, it's not too hard to
imagine that such over-eager error checking might break perfectly
sensible code.

Matthias Blume

unread,
May 14, 2002, 5:23:59 PM5/14/02
to

Huh? How can a 0 in operator position *ever* be useful in *any*
Scheme program? Answer: never. Why? Because if the non-local exit
is always taken (which is the only way that the program can avoid a
runtime type error), then the code in question is never executed
anyway, so it does not need to be there.

Barry Margolin has already replied to the "automatically generated
code" claim. It is obviously bogus and no excuse for accepting
expressions of the form (0 ...).

--
-Matthias

Matthias Blume

unread,
May 14, 2002, 5:25:27 PM5/14/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

Ok, show me *one* example of "perfectly sensible" code that would
break and which could not trivially be fixed.

--
-Matthias

Ray Dillinger

unread,
May 14, 2002, 5:54:44 PM5/14/02
to

Matthias Blume wrote:

> > What? You think it's perfectly peachy if a compiler generates
> > incorrect code for valid input?

> No, I think it is perfectly peachy if a compiler does not generate any
> code at all for programs that are "obviously wrong" (for certain
> values of "obviously wwrong" :-). Having a function call with
> something in operator position that is statically known to never be a
> function is "obviously wrong" since either the program never tries to
> perform the function call (in which case it does not need to be there
> in the first place) or it is certain to run afoul a runtime error.

The object is *called*, just fine; it won't
create an error unless an attempt is made to
*apply* it. There is no real distinction
between being called and being applied in
languages that don't have a call/cc or similar
construct; but scheme has one.

This is bizarre and hacky, but it's well within
the mandate of call/cc to facilitate "unusual
paths of control" and I can imagine (albeit
with a shudder) a human producing code that
works like this on purpose. Although she'd
probably substitute the string "function name"
for the 0 in the expression above...

Bear

---
"We could have an obfuscated-scheme contest, but
if call/cc and macros are allowed, that would be like
using nuclear bombs to hunt bunnyrabbits..."

Thomas Bushnell, BSG

unread,
May 14, 2002, 6:54:26 PM5/14/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> Huh? How can a 0 in operator position *ever* be useful in *any*
> Scheme program? Answer: never. Why? Because if the non-local exit
> is always taken (which is the only way that the program can avoid a
> runtime type error), then the code in question is never executed
> anyway, so it does not need to be there.

I said consider automatically generated code. If it's not literal "0"
but instead something produced computationally, then it's perfectly
reasonable to consider.

Scheme is not a typed variable or typed expression language. It is
perfectly reasonable to have an expression which sometimes generates
procedures and sometimes not, and perfectly reasonable to put that
into the call position of a combination as long as you know that in
the cases where it generates a non-procedure, the call won't be
attempted.


Thomas Bushnell, BSG

unread,
May 14, 2002, 7:10:49 PM5/14/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> Ok, show me *one* example of "perfectly sensible" code that would
> break and which could not trivially be fixed.

I suggest you add the following to the compiler documentation:

"The authors of this compiler have decided that certain legal Scheme
code is not sensible, and therefore have chosen not to compile it."

Thomas

Thomas Bushnell, BSG

unread,
May 14, 2002, 7:10:02 PM5/14/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> Ok, show me *one* example of "perfectly sensible" code that would
> break and which could not trivially be fixed.

Here's a sketch rather than actual code.

One might implement a command parser in the following way. The first
word is looked up with

(lookup-command WORD)

WORD must be a string. LOOKUP-COMMAND returns #f if that word is not
found, otherwise it returns a procedure for the word. This is a
perfectly natural thing to do.

Arguments are looked up with

(lookup-argument WORD)

This converts arguments into some kind of internal form. Any string
is a valid argument.

And we execute commands with forms something like the following:

(let* ((word1 (read-word))
(word2 (read-word)))
((lookup-command word1) (lookup-argument word2)))

Ok, so far so good. If the user types a command that does not exist,
they get an error from this form. This is what we want.

However, the language we are interpreting allows for some arguments to
be programmatic (think of Unix shell backquote syntax, maybe). For
some WORDs, lookup-argument will execute arbitrary code. And that
code--provided by the user, will perhaps reboot the machine or
otherwise escape from the local control of the key combination above.

Now of course, we could order the user never to type such commands,
but they are perfectly reasonable. We could of course obfuscate the
clarity of the above design by having lookup-command generate a
function instead of the clearer #f; we could do various things. But
they all make the structure less clear, not moreso.

You might say "but a user should never type such a command". Ah, but
that's wrong too. Suppose there are nifty things you can do in an
argument context but not in a command context. A user who wants
access to those things should feel free to type:

bogus-fake-command fancy-schmancy-argument

provided he knows that fancy-schmancy-argument will also exit from the
local context.

Thomas

David Rush

unread,
May 14, 2002, 8:46:40 PM5/14/02
to
"felix" <felixu...@freenet.de> writes:
> felix wrote in message <3ce0e6c5$0$350$9b62...@news.freenet.de>...
> >Another test for proper TCO might be:
> Actually this can be reduced to
>
> (letrec ((chain
...
> )))

> I just tested this with Stalin, though.

and the result was?

david rush
--
Thieves respect property. They merely wish the property to become
their property that they may more perfectly respect it.
-- The Man Who Was Thursday (G. K. Chesterton)

David Rush

unread,
May 14, 2002, 8:50:54 PM5/14/02
to
Alfresco Petrofsky <alfr...@petrofsky.org> writes:
> And I did properly credit
> Bawden in that post, citing the full text of his February 1988 message
> (which consisted of just a single "!" as the subject line, and 17
> lines of code as the body). I even included the old message-id,
> <323426.8...@AI.AI.MIT.EDU>, in the references header of my
> article, for the convenience of those using a threaded reader with a
> very large spool.

And this is just that level of attention to detail which we so
appreciate in your work. Keep at it ;)

david rush
--
With guns, we are citizens. Without them, we are subjects.
-- YZGuy, IPL

Bruce Hoult

unread,
May 14, 2002, 10:19:22 PM5/14/02
to
In article <fog00uf...@blume-pcmh.research.bell-labs.com>,
Matthias Blume <matt...@shimizu-blume.com> wrote:

> > The point is that your disjunction fails in any language that allows
> > non-local exits, particularly given the possibility of automatically
> > generated code.
>
> Huh? How can a 0 in operator position *ever* be useful in *any*
> Scheme program? Answer: never. Why? Because if the non-local exit
> is always taken (which is the only way that the program can avoid a
> runtime type error), then the code in question is never executed
> anyway, so it does not need to be there.

It's not useful in itself, but a very good argument can be made that,
when doing exploratory programming in a dynamic language, you should be
able to run the program and test parts of it that are OK even if other
parts of it are incorrect.

It is also a good principle that a compiler should not change the
semantics of a program. A simple interpreter will produce the desired
result here, and so will a good compiler such as Gwydion Dylan which
optimizes out the continuation entirely [1]. I'm sure Stalin will be
doing the same.

It seems to me that a half-smart compiler which realizes that 0 is not a
function, but doesn't notice that it will never be called is a very bad
intermediate position. A warning might be appropriate. Inserting
unconditional code which aborts at runtime -- *after* evaluating the
arguments -- would be OK. Giving a compile error and refusing to run
the program is *very* bad because the program will in fact not get an
error if run.

It's OK to transfer errors from runtime to compile time, but *only* if
you are completely certain that the error will actually occur.

Well that's the philisophy in the Dylan community, anyway.

-- Bruce


[1] Dylan's continuations are less powerful than Scheme's in that they
can only be called once and can't escape from the creating scope, but in
this case a good Scheme compiler won't have any trouble proving those
properties anyway.

Al Petrofsky

unread,
May 14, 2002, 11:07:17 PM5/14/02
to
Bruce Hoult <br...@hoult.org> writes:

> It's not useful in itself, but a very good argument can be made that,
> when doing exploratory programming in a dynamic language, you should be
> able to run the program and test parts of it that are OK even if other
> parts of it are incorrect.
>
> It is also a good principle that a compiler should not change the
> semantics of a program.

I'm with you so far.

> It's OK to transfer errors from runtime to compile time, but *only* if
> you are completely certain that the error will actually occur.

You should stick to your principles. In the program (begin (write
"Hello") (-)), one can be certain that a runtime error will occur, but
not until after the program says hello. It is bogus to report this as
a compile-time error and refuse to execute the program. Similarly, in
the program (0), one can be certain that a runtime error will occur,
but not until the program is run! In other words, if compile-time and
run-time are two different times, then compile-time and run-time
errors are two different things, and the compiler should not be
treating them as the same thing. If the compiler can determine that a
runtime error is inevitable, then it can certainly be useful for it to
report that, but it should generate the code, too.

-al

Matthias Blume

unread,
May 14, 2002, 11:10:10 PM5/14/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

Consider it added. The documentation now devotes as much space to
this issue as it devotes to everything else.

--
-Matthias

PS: There is no documentation. :-)

Matthias Blume

unread,
May 14, 2002, 11:04:07 PM5/14/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > Huh? How can a 0 in operator position *ever* be useful in *any*
> > Scheme program? Answer: never. Why? Because if the non-local exit
> > is always taken (which is the only way that the program can avoid a
> > runtime type error), then the code in question is never executed
> > anyway, so it does not need to be there.
>
> I said consider automatically generated code. If it's not literal "0"
> but instead something produced computationally, then it's perfectly
> reasonable to consider.

No, it is perfectly unreasonable to me.

> Scheme is not a typed variable or typed expression language. It is
> perfectly reasonable to have an expression which sometimes generates
> procedures and sometimes not,

But the expression in question *never* creates a procedure.

> and perfectly reasonable to put that
> into the call position of a combination as long as you know that in
> the cases where it generates a non-procedure, the call won't be
> attempted.

But if it never creates a procedure, then it better *never* be called -- in
which case it does not need to be there at all.

I really don't see why anyone would want to fight for such a
completely bogus "feature".

--
-Matthias

Matthias Blume

unread,
May 14, 2002, 11:07:38 PM5/14/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

What would such a "nifty" thing be that can only be done in argument
context?

> bogus-fake-command fancy-schmancy-argument
>
> provided he knows that fancy-schmancy-argument will also exit from the
> local context.

If he knows that, why does he type "bogus-fake-command" and not some
other, real command? It won't be executed anyway, so you can write
*anything*, including something that is actually legal, right?

Besides, this has nothing to do with the original problem.

--
-Matthias

Matthias Blume

unread,
May 14, 2002, 11:39:29 PM5/14/02
to
Al Petrofsky <a...@petrofsky.org> writes:

Why?

If the programmer *really* intends the program to run until this point
and then crash, she could have *programmed* the crash some other way,
now couldn't she? So if the compiler refuses the program, it is
absolutely trivial to go back and change

(0)

to, say,

(error ...)

This is cleaner since it clearly expresses the intention of the
programmer and it does not abuse an incidental property of how numbers
in operator position supposedly have to behave.

--
-Matthias

Matthias Blume

unread,
May 14, 2002, 11:33:40 PM5/14/02
to
Bruce Hoult <br...@hoult.org> writes:

> In article <fog00uf...@blume-pcmh.research.bell-labs.com>,
> Matthias Blume <matt...@shimizu-blume.com> wrote:
>
> > > The point is that your disjunction fails in any language that allows
> > > non-local exits, particularly given the possibility of automatically
> > > generated code.
> >
> > Huh? How can a 0 in operator position *ever* be useful in *any*
> > Scheme program? Answer: never. Why? Because if the non-local exit
> > is always taken (which is the only way that the program can avoid a
> > runtime type error), then the code in question is never executed
> > anyway, so it does not need to be there.
>
> It's not useful in itself, but a very good argument can be made that,
> when doing exploratory programming in a dynamic language, you should be
> able to run the program and test parts of it that are OK even if other
> parts of it are incorrect.

Exploratory programming in this sort of environment is easy: The
compiler tells you right away that this is obviously either completely
bogus or doesn't do any good at runtime anyway, so you can delete it.
If you need a placeholder there, use the identity function, or a
function that explictly aborts execution...

As a matter of fact, the original VSCM compiler didn't do this level
of checking. A added stricter checking when I added the module
system. The resulting improvements in my own productivity due to
early error detection were very noticable. In fact, they were so
noticable that they made me wish for /even stricter/ static error
checking -- which is why nowadays I program in SML and not in Scheme.

By the way, your "very good argument" (which I personally do not find
all that good) falls flat on its face with a number of other errors
(e.g., syntax errors). Or would you argue that a compiler should
compile, say,

(if #t 1 (lambda))

because the syntax error is in the portion of the expression that does
not get evaluated anyway? After all, a simple brain-dead interpreter
might actually succeed in evaluating this thing to 1....

> It is also a good principle that a compiler should not change the
> semantics of a program.

By rejecting a program, the compiler does not change the semantics of
it. It merely refuses to accept a program with this semantics.

The VSCM compiler in question does not stick to RnRS semantics anyway
since it adds a module system with associated changes to the semantics
of free ("global") variables etc. Besides, I personally consider a
program with a literal 0 in operator position to be meaningless, so
rejecting it most certainly does not change its (non-existent) meaning.

> It seems to me that a half-smart compiler which realizes that 0 is not a
> function, but doesn't notice that it will never be called is a very bad
> intermediate position.

It clearly is in an intermediate position. The label "very bad" is
highly subjective. In my own work, I found this feature extremely
valuable, aka "very good".

> A warning might be appropriate.

Right. But to me, a warning is just the same as a compile-time error.

> Inserting unconditional code which aborts at runtime -- *after*
> evaluating the arguments -- would be OK.

What does it buy you?

> Giving a compile error and refusing to run the program is *very*
> bad because the program will in fact not get an error if run.

I do not see why this would be "very bad". After all, the program
contains a highly suspicious construct which is completely useless.

> It's OK to transfer errors from runtime to compile time, but *only* if
> you are completely certain that the error will actually occur.

In most cases I would agree with this. But here we have a construct
that can be only in one of two states: always useless or error. In
this situation, I am more than happy if the compiler tells me about it
because having such a thing in my program is definitely something
I did not intend.

--
-Matthias

Thomas Bushnell, BSG

unread,
May 15, 2002, 1:01:31 AM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> If the programmer *really* intends the program to run until this point
> and then crash, she could have *programmed* the crash some other way,
> now couldn't she?

What? Not if eager compiler writers carefully detect all possible
crashes and refuse to emit the requested code.

I've written valid C code with the following:

while (1)
{
volatile int one=1, zero=0;
one/zero;
}

and it was damn important that the compiler emitted the requested
code.

Thomas Bushnell, BSG

unread,
May 15, 2002, 1:31:15 AM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> By the way, your "very good argument" (which I personally do not find
> all that good) falls flat on its face with a number of other errors
> (e.g., syntax errors). Or would you argue that a compiler should
> compile, say,
>
> (if #t 1 (lambda))
>
> because the syntax error is in the portion of the expression that does
> not get evaluated anyway? After all, a simple brain-dead interpreter
> might actually succeed in evaluating this thing to 1....

The standard treats bad syntax differently from other errors.

There is no correct compilation of (lambda) according to R5RS.
According to section 7.2.1, (lambda) is not a valid expression at
all. It is correct to refuse to compile it.

By contrast, (0) is a valid expression; its the third type specified
in 7.2.1. The correct way to compile it is into
wrong "bad procedure"
which is a well-defined piece of semantics--it is implementation
dependent indeed, but the type of the semantic function is perfectly
well defined (X -> C). You are free to make that mapping whatever you
choose, but you are not free to omit it entirely.

> Right. But to me, a warning is just the same as a compile-time error.

This is your opinion as a user. Why should it be foisted on every
piece of code generated by everyone? The whole point of warnings is
that they are dangerous, but not wrong pieces of code. I agree that
in writing code it is usually right to avoid warnings, but the whole
point of warnings is that they are *not* errors.

More to the point, the following *must* *not* produce an error:

(lambda () (0))

It is an error to call such a procedure, but it is *not* an error to
create one. If your idea of "cleanliness" means that a warning is as
bad as an error, then you should not issue a warning either. Nothing
in the standard requires or even suggests such warnings.

> In most cases I would agree with this. But here we have a construct
> that can be only in one of two states: always useless or error. In
> this situation, I am more than happy if the compiler tells me about it
> because having such a thing in my program is definitely something
> I did not intend.

To repeat, I have found sure-to-generate-error code to be useful. The
following C code:

while (1) { volatile int zero=0, one=1; one/zero; }

was important in its context, which was a context where it would have
been *wrong* to do something like "raise (SIGFPE)".

Thomas


Thomas Bushnell, BSG

unread,
May 15, 2002, 1:37:59 AM5/15/02
to

I guess the real question is: why bother implementing Scheme if your
attitude is "the Scheme standard sucks horribly and should be
ignored".

Thomas Bushnell, BSG

unread,
May 15, 2002, 1:37:29 AM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> What would such a "nifty" thing be that can only be done in argument
> context?

In the scenario given you are implementing an already-given language
which has a misfeature. If you've ever dealt with "little languages",
you've encountered all such warts and non-orthogonalities.

> > bogus-fake-command fancy-schmancy-argument
> >
> > provided he knows that fancy-schmancy-argument will also exit from the
> > local context.
>
> If he knows that, why does he type "bogus-fake-command" and not some
> other, real command? It won't be executed anyway, so you can write
> *anything*, including something that is actually legal, right?

Because he wants to make *clear to the reader* that it's not going to
be called.

Thomas

Thomas Bushnell, BSG

unread,
May 15, 2002, 1:34:26 AM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> (if #t 1 (lambda))
>
> because the syntax error is in the portion of the expression that does
> not get evaluated anyway? After all, a simple brain-dead interpreter
> might actually succeed in evaluating this thing to 1....

Yes, but this is specified by the standard to be incorrect syntax.
Why? Because the only legitimate syntax for an IF form is:

(if E0 E1 E2)

but E2 must be an expression, and (lambda) is not an expression.

[See 7.2.1]

So the standard does not specify at all what to do if that form is
encountered. The standard *does* specify what to do if the form
(lambda () (0))
is encountered, and if you walk through the semantics, you will see
that it nowhere generates an error.


Thomas Bushnell, BSG

unread,
May 15, 2002, 1:36:06 AM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> I really don't see why anyone would want to fight for such a
> completely bogus "feature".

Because it makes the semantics *cleaner* and *more transparent*.
Think about it a minute and see what would be involved to incorporate
such things into the semantics. It's actually *very* difficult.
That's why the semantics are as they are. It is a feature of scheme
that the formal semantics are clear and simple.

But now you are saying that R5RS should be changed. Do you have an
amendment to propose?

Saying that it's fine to ignore perfectly well-defined semantics is
entirely different from saying that you think a different set of
semantics would be better.

Thomas

felix

unread,
May 15, 2002, 4:59:34 AM5/15/02
to

David Rush wrote in message ...

>"felix" <felixu...@freenet.de> writes:
>> felix wrote in message <3ce0e6c5$0$350$9b62...@news.freenet.de>...
>> >Another test for proper TCO might be:
>> Actually this can be reduced to
>>
>> (letrec ((chain
>...
>> )))
>
>> I just tested this with Stalin, though.
>
>and the result was?
>


Segmentation fault

(I'm using Stalin 0.9 on a PII running Mandrake Linux 7.2)


cheers,
felix


Matthias Blume

unread,
May 15, 2002, 6:31:46 AM5/15/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

Maybe you could elaborate on why the above would be "damn important".

--
-Matthias

Matthias Blume

unread,
May 15, 2002, 6:44:13 AM5/15/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > (if #t 1 (lambda))
> >
> > because the syntax error is in the portion of the expression that does
> > not get evaluated anyway? After all, a simple brain-dead interpreter
> > might actually succeed in evaluating this thing to 1....
>
> Yes, but this is specified by the standard to be incorrect syntax.
> Why?

[ explanation elided ]

I know the "standard". I do not think that the standard is
particularly useful in making such a fundamental a distinction between
a syntax error and an error caused by the expression (0). As a matter
of fact, the reason VSCM gives for rejecting (0) is "syntax error"
because (unlike the standard) it uses a notion of syntax where a
literal in operator position is *syntactically* illegal.

For the last time: In this particular case I DONT CARE ABOUT WHAT THE
STANDARD SAYS because I do not consider expressions such as (0) to
*ever* be useful in practice.

--
-Matthias

Matthias Blume

unread,
May 15, 2002, 6:39:40 AM5/15/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > By the way, your "very good argument" (which I personally do not find
> > all that good) falls flat on its face with a number of other errors
> > (e.g., syntax errors). Or would you argue that a compiler should
> > compile, say,
> >
> > (if #t 1 (lambda))
> >
> > because the syntax error is in the portion of the expression that does
> > not get evaluated anyway? After all, a simple brain-dead interpreter
> > might actually succeed in evaluating this thing to 1....
>
> The standard treats bad syntax differently from other errors.
>
> There is no correct compilation of (lambda) according to R5RS.
> According to section 7.2.1, (lambda) is not a valid expression at
> all. It is correct to refuse to compile it.

Yes. I know the standard. All I said was that it comes up short
according to your "exploratory programming" claim. To me, it is ok to
reject this code, and so is rejecting (0 ...).

> By contrast, (0) is a valid expression; its the third type specified
> in 7.2.1. The correct way to compile it is into
> wrong "bad procedure"
> which is a well-defined piece of semantics--it is implementation
> dependent indeed, but the type of the semantic function is perfectly
> well defined (X -> C). You are free to make that mapping whatever you
> choose, but you are not free to omit it entirely.

Why not? I am free to do whatever I like. I think that (0) is 99% of
the time not what was intended, so 99% of the time it is helpful to
reject the code. In the remaining 1% of the time (if that often), it
is absolutely trivial to avoid the "problem".

> > Right. But to me, a warning is just the same as a compile-time error.
>
> This is your opinion as a user. Why should it be foisted on every
> piece of code generated by everyone? The whole point of warnings is
> that they are dangerous, but not wrong pieces of code.

But in this particular case it is "wrong" because the code either
causes a runtime error (i.e., is "wrong") or it is useless (hence
"wrong").

> More to the point, the following *must* *not* produce an error:
>
> (lambda () (0))

It must not produce a runtime error. In my implementation it doesn't. :-)

> > In most cases I would agree with this. But here we have a construct
> > that can be only in one of two states: always useless or error. In
> > this situation, I am more than happy if the compiler tells me about it
> > because having such a thing in my program is definitely something
> > I did not intend.
>
> To repeat, I have found sure-to-generate-error code to be useful. The
> following C code:
>
> while (1) { volatile int zero=0, one=1; one/zero; }
>
> was important in its context, which was a context where it would have
> been *wrong* to do something like "raise (SIGFPE)".

What was this context? For low-level things like this where the C
standard does not even give a clear indication of what's supposed to
happen, I personally would prefer to write a line or two of assembler.

--
-Matthias

Matthias Blume

unread,
May 15, 2002, 6:47:03 AM5/15/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > I really don't see why anyone would want to fight for such a
> > completely bogus "feature".
>
> Because it makes the semantics *cleaner* and *more transparent*.
> Think about it a minute and see what would be involved to incorporate
> such things into the semantics. It's actually *very* difficult.

Nonsense. All that is needed is to make literal constants in operator
position illegal. One does not even need to go into semantics for this.

> That's why the semantics are as they are. It is a feature of scheme
> that the formal semantics are clear and simple.
>
> But now you are saying that R5RS should be changed. Do you have an
> amendment to propose?

I said that R5RS should be changed *many* *many* times. (For other
reasons, though.)

> Saying that it's fine to ignore perfectly well-defined semantics is
> entirely different from saying that you think a different set of
> semantics would be better.

In this case all I ask for is a slightly altered definition of valid
syntax.

--
-Matthias

Matthias Blume

unread,
May 15, 2002, 6:52:56 AM5/15/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

That is, indeed, a question. I do not think that the Scheme standard
"sucks horribly", although some aspects of it do (IMO). Notice that
VSCM was implemented about 8 years ago, when the author (me) was still
a bit more sympathetic to the prevalent Scheme philosophy. But this
has very little to do with the problem at hand.

In any case, I did not (and still do not) consider rejecting things
like (0) a major departure form the holy scripture. In fact, I
couldn't imagine (until two days ago) that there would be any serious
argument over this at all.

--
-Matthias

ozan s yigit

unread,
May 15, 2002, 9:40:27 AM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> In any case, I did not (and still do not) consider rejecting things
> like (0) a major departure form the holy scripture.

i agree, and i would also note that the holy scripture (IEEE) was left
in the bottom of a locked filing cabinet stuck in a disused lavatory with
a sign outside the door saying "Beware of the Authors." that other R*RS
document is just a placebo.

oz
--
you take a banana, you get a lunar landscape. -- j. van wijk

ozan s yigit

unread,
May 15, 2002, 9:47:11 AM5/15/02
to
tb+usenet in response to matthias:

> But now you are saying that R5RS should be changed. Do you have an
> amendment to propose?

i suspect there may be a number of changes pending. there is a meeting
coming up during PLDI conference (i think) which may be reflected in the
next RS. [my hope is that IEEE process can get bootstrapped also.]

oz
--
grasshopper, you have not yet grasped the lambda-nature. -- david rush

ozan s yigit

unread,
May 15, 2002, 10:11:28 AM5/15/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

> Because he wants to make *clear to the reader* that it's not going to
> be called.

correct way to handle this was illustrated before; instead of a constant
an valid function (named appropriately to indicate that it is not going to
be called, so no puzzles) can be used that can report "cannot happen." by
now it is clear that compilers don't agree on how to handle a constant
where a function is needed; when automatically generating code, less
silly would go farther...

oz
---
fantastically elaborate cliches are still cliches. -- john shirley

Barry Margolin

unread,
May 15, 2002, 11:35:06 AM5/15/02
to
In article <87wuu62...@becket.becket.net>,
Thomas Bushnell, BSG <tb+u...@becket.net> wrote:

>Matthias Blume <matt...@shimizu-blume.com> writes:
>
>> Huh? How can a 0 in operator position *ever* be useful in *any*
>> Scheme program? Answer: never. Why? Because if the non-local exit
>> is always taken (which is the only way that the program can avoid a
>> runtime type error), then the code in question is never executed
>> anyway, so it does not need to be there.
>
>I said consider automatically generated code. If it's not literal "0"
>but instead something produced computationally, then it's perfectly
>reasonable to consider.

But if it's something produced computationally, the compiler can't reject
it, since this value won't be known until run-time. The issue is *only*
with a literal that can never evaluate to procedures being put in the
procedure position of an expression. It had to come from somewhere, and
whoever put it there should have known that it was going to end up in a
procedure position eventually, so should have put something that is
syntactically valid there.

--
Barry Margolin, bar...@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.

Barry Margolin

unread,
May 15, 2002, 11:46:41 AM5/15/02
to
In article <bruce-8E132D....@copper.ipg.tsnz.net>,

Bruce Hoult <br...@hoult.org> wrote:
>In article <fog00uf...@blume-pcmh.research.bell-labs.com>,
> Matthias Blume <matt...@shimizu-blume.com> wrote:
>
>> > The point is that your disjunction fails in any language that allows
>> > non-local exits, particularly given the possibility of automatically
>> > generated code.
>>
>> Huh? How can a 0 in operator position *ever* be useful in *any*
>> Scheme program? Answer: never. Why? Because if the non-local exit
>> is always taken (which is the only way that the program can avoid a
>> runtime type error), then the code in question is never executed
>> anyway, so it does not need to be there.
>
>It's not useful in itself, but a very good argument can be made that,
>when doing exploratory programming in a dynamic language, you should be
>able to run the program and test parts of it that are OK even if other
>parts of it are incorrect.

How much harder is it to write

(f (cont x))

instead of

(0 (cont x))

Even when doing exploratory programming, you're program should be
syntactically reasonable, and this is usually not difficult to achieve.
It's also quite common to put stubs in place of unfinished code while
exploring or debugging, so a good way to do this exploration would be:

(error (cont x))

If it reports the error, your debugging session has helped you -- you
learned that the call that should never returned did so for some reason.

Marc Feeley

unread,
May 15, 2002, 12:03:41 PM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> Huh? How can a 0 in operator position *ever* be useful in *any*
> Scheme program? Answer: never. Why? Because if the non-local exit
> is always taken (which is the only way that the program can avoid a
> runtime type error), then the code in question is never executed
> anyway, so it does not need to be there.

I find this discussion funny because if you look in the Gambit-C
source code, in _kernel.scm, there are in fact a few calls like this:
(0) and they are "useful". Indeed, these are used in non-tail-call
position to generate a kind of a code label (a "return point"). A
call to 0 is never performed, but jumps to the return points are
possible. These return points correspond to internal "functions" of
the Gambit-C kernel (to handle rest parameters, to signal wrong number
of argument exceptions, etc).

So it would be a problem if the compiler refused to compile that code.

Marc

Al Petrofsky

unread,
May 15, 2002, 12:08:35 PM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> Al Petrofsky <a...@petrofsky.org> writes:
>
> > In other words, if compile-time and run-time are two different
> > times, then compile-time and run-time errors are two different
> > things, and the compiler should not be treating them as the same
> > thing. If the compiler can determine that a runtime error is
> > inevitable, then it can certainly be useful for it to report that,
> > but it should generate the code, too.
>

> If the programmer *really* intends the program to run until this point
> and then crash, she could have *programmed* the crash some other way,
> now couldn't she? So if the compiler refuses the program, it is

> absolutely trivial to go back and change [the code]

I can certainly see the advantages of having a staticly-typed language
in which there are only compile-time type errors and no runtime type
errors. Scheme, however, is a dynamicly-typed language, in which
there are only runtime type errors and no compile-time type errors
(and no type-matching burden, trivial or otherwise, for the programmer
to get a program to compile). I prefer this to having a language with
lots of runtime type errors and one compile-time type error.

-al

Matthias Blume

unread,
May 15, 2002, 12:40:40 PM5/15/02
to
Al Petrofsky <a...@petrofsky.org> writes:

In VSCM, it is a *syntax* error, not a type error.

--
-Matthias

Matthias Blume

unread,
May 15, 2002, 12:40:01 PM5/15/02
to
Marc Feeley <fee...@IRO.UMontreal.CA> writes:

I cannot imagine that there wouldn't have been another, cleaner way of
achieving this. In any case, what you are doing is clearly
implementation-dependent. I do not see why *another* compiler such as
VSCM should accept such code for the reason you state.

--
-Matthias

Barry Margolin

unread,
May 15, 2002, 1:19:12 PM5/15/02
to
In article <q6y9elo...@dino00.iro.umontreal.ca>,

Marc Feeley <fee...@IRO.UMontreal.CA> wrote:
>I find this discussion funny because if you look in the Gambit-C
>source code, in _kernel.scm, there are in fact a few calls like this:
>(0) and they are "useful". Indeed, these are used in non-tail-call
>position to generate a kind of a code label (a "return point"). A
>call to 0 is never performed, but jumps to the return points are
>possible. These return points correspond to internal "functions" of
>the Gambit-C kernel (to handle rest parameters, to signal wrong number
>of argument exceptions, etc).
>
>So it would be a problem if the compiler refused to compile that code.

If I understand you correctly, these things are being recognized specially
by the compiler, and being used as something other than a function call.
It's taking advantage of the fact that they don't have any standard
meaning, and assigning some new, implementation-dependent meaning to them.

Since another implementation isn't going to treat these as "code labels",
they won't work in another compiler. So the fact that some other compiler
refuses to compile them seems like it would be the least of your problems
in trying to port that code.

Barry Margolin

unread,
May 15, 2002, 1:29:49 PM5/15/02
to
In article <87znz11...@radish.petrofsky.org>,

Al Petrofsky <a...@petrofsky.org> wrote:
>I can certainly see the advantages of having a staticly-typed language
>in which there are only compile-time type errors and no runtime type
>errors. Scheme, however, is a dynamicly-typed language, in which
>there are only runtime type errors and no compile-time type errors
>(and no type-matching burden, trivial or otherwise, for the programmer
>to get a program to compile). I prefer this to having a language with
>lots of runtime type errors and one compile-time type error.

Even in dynamically-typed languages, the types of literals can be trivially
determined at compile time (and via data-flow analysis, the types of some
non-literal expressions as well), and compilers are generally allowed (even
expected) to take advantage of this, for both optimization and error
checking. While many errors can only be detected at run-time, that doesn't
mean we shouldn't try to detect the ones that can be found at compile-time.

Consider:

(define (stupid-function x)
(+ #f x))

It's true that if stupid-function is never called, an error will never
occur. But I'll happily use a compiler that refuses to compile the
function. If the function can never do anything useful if it's called,
what's the point of defining it in the first place? While I don't like the
compiler to tie my hands, I don't mind it assisting me by pointing out
bogus code.

Al Petrofsky

unread,
May 15, 2002, 2:03:44 PM5/15/02
to

Compile-time type errors *are* syntax errors, aren't they? Aren't you
proposing a syntax something like this:

<expr>: <type1-expr> | <type2-expr>
<type1-expr>: <combination> | <lambda> | <var-ref>
<type2-expr>: <assignment> | <quotation> | <literal>
<combination>: (<type1-expr> <expr> ...)
<lambda>: (lambda (<identifier> ...) <expr> <expr> ...)
<var-ref>: <identifier>
<assignment>: (set! <identifier> <expr>)
<quotation>: (quote <datum>)
<literal>: <number> | <boolean> | <character> | <string>

And then (0) is a syntax error, because the operator expression has
the wrong compile-time type. Contrast this to the r5rs syntax, in
which all productions that contain subexpressions use the same
<expression> type for those expressions, hence there are no
compile-time type errors.

-al

Al Petrofsky

unread,
May 15, 2002, 2:29:50 PM5/15/02
to
Barry Margolin <bar...@genuity.net> writes:

> While many errors can only be detected at run-time, that doesn't
> mean we shouldn't try to detect the ones that can be found at
> compile-time.

We've all stated our agreement that it is helpful for the compiler to
detect this situation. The question is whether it should reject the
code or issue a warning.

> I'll happily use a compiler that refuses to compile the function.

> ... While I don't like the compiler to tie my hands, I don't mind it


> assisting me by pointing out bogus code.

I'm a bit confused by you making a distinction between hand-tying and
assistance, but then placing both rejections and warnings in the
latter category.

-al

Matthias Blume

unread,
May 15, 2002, 2:33:01 PM5/15/02
to
Al Petrofsky <a...@petrofsky.org> writes:

> Matthias Blume <matt...@shimizu-blume.com> writes:
> > Al Petrofsky <a...@petrofsky.org> writes:
> >
> > > I can certainly see the advantages of having a staticly-typed
> > > language in which there are only compile-time type errors and no
> > > runtime type errors. Scheme, however, is a dynamicly-typed
> > > language, in which there are only runtime type errors and no
> > > compile-time type errors (and no type-matching burden, trivial or
> > > otherwise, for the programmer to get a program to compile). I
> > > prefer this to having a language with lots of runtime type errors
> > > and one compile-time type error.
> >
> > In VSCM, it is a *syntax* error, not a type error.
>
> Compile-time type errors *are* syntax errors, aren't they?

While type checking in the general sense is a syntactic operation (as
is everything else that you can do with a computer), this is not how
we usually think and talk about it. In statically typed languages,
type errors are found during semantic analysis -- a phase that tends
to be separate from syntactic analysis (the latter being where syntax
errors are found).

> Aren't you
> proposing a syntax something like this:
>
> <expr>: <type1-expr> | <type2-expr>
> <type1-expr>: <combination> | <lambda> | <var-ref>
> <type2-expr>: <assignment> | <quotation> | <literal>
> <combination>: (<type1-expr> <expr> ...)
> <lambda>: (lambda (<identifier> ...) <expr> <expr> ...)
> <var-ref>: <identifier>
> <assignment>: (set! <identifier> <expr>)
> <quotation>: (quote <datum>)
> <literal>: <number> | <boolean> | <character> | <string>
>
> And then (0) is a syntax error, because the operator expression has
> the wrong compile-time type.

No. It does not have "the wrong compile-time type" but simply cannot
be derived from the production rule set that you gave.

> Contrast this to the r5rs syntax, in
> which all productions that contain subexpressions use the same
> <expression> type for those expressions, hence there are no
> compile-time type errors.

For the last time: I *know* the RnRS syntax, and I *deliberately*
implemented a recognizer for a slightly different syntax in my
compiler -- for the purpose of statically rejecting certain programs
that do not have any conceivable purpose (other than providing fuel
for netnews flamewars :-).

--
-Matthias

Barry Margolin

unread,
May 15, 2002, 2:57:42 PM5/15/02
to
In article <87lmal1...@radish.petrofsky.org>,
Al Petrofsky <a...@petrofsky.org> wrote:

>Barry Margolin <bar...@genuity.net> writes:
>> I'll happily use a compiler that refuses to compile the function.
>> ... While I don't like the compiler to tie my hands, I don't mind it
>> assisting me by pointing out bogus code.
>
>I'm a bit confused by you making a distinction between hand-tying and
>assistance, but then placing both rejections and warnings in the
>latter category.

Hand-tying is when a language or implementation prevents you from doing
useful things (or makes it difficult to do them) because the language
designer or implementor didn't think they were good ideas.

Rejecting code that makes no sense doesn't fall into that category.

Al Petrofsky

unread,
May 15, 2002, 3:20:26 PM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:
> Al Petrofsky <a...@petrofsky.org> writes:
>
> > Aren't you proposing a syntax something like this:

> > And then (0) is a syntax error, because the operator expression has
> > the wrong compile-time type.

> > Contrast this to the r5rs syntax, in which all productions that


> > contain subexpressions use the same <expression> type for those
> > expressions, hence there are no compile-time type errors.
>
> For the last time: I *know* the RnRS syntax, and I *deliberately*
> implemented a recognizer for a slightly different syntax

I don't think I ever implied otherwise. I said "Aren't you
[deliberately] proposing a syntax" different from r5rs. I was then
attempting to explain what I mean by "no compile-time type errors", a
property possessed (for better or for worse) by the r5rs syntax but
not by your deliberately different one.

-al

Thomas Bushnell, BSG

unread,
May 15, 2002, 3:47:00 PM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> > More to the point, the following *must* *not* produce an error:
> >
> > (lambda () (0))
>
> It must not produce a runtime error. In my implementation it doesn't. :-)

Incorrect. To call the function *must* generate a runtime error, and
in your implementation it doesn't. To evaluate the lambda *must not*.

I'd bet that I can generate the incorrect error behavior on your
system by embedding the cases in eval...

Thomas Bushnell, BSG

unread,
May 15, 2002, 3:44:27 PM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> Maybe you could elaborate on why the above would be "damn important".

It was in the implementation of signal vectoring in the Hurd. Among
other things, this is the normal way to implement the abort() function
in C. It is important that abort() never proceed to its continuation
even if everything else in the system is broken. For that purpose, of
course, "while (1);" would be sufficient. Generating a machine
exception is better, because it gives a possibly-malfuctioning
system--or a debugger--a real chance.

Thomas Bushnell, BSG

unread,
May 15, 2002, 3:48:55 PM5/15/02
to
ozan s yigit <o...@blue.cs.yorku.ca> writes:

> correct way to handle this was illustrated before; instead of a constant
> an valid function (named appropriately to indicate that it is not going to
> be called, so no puzzles) can be used that can report "cannot happen." by
> now it is clear that compilers don't agree on how to handle a constant
> where a function is needed; when automatically generating code, less
> silly would go farther...

Compilers do not define the language semantics--everybody but one
wacko whose compiler gets this wrong has said "oops, weird bug, never
thought of that one" and plans to fix it.

We do *NOT* establish "valid scheme" by conducting a poll of what
various compilers happen to do.

Thomas Bushnell, BSG

unread,
May 15, 2002, 3:50:14 PM5/15/02
to
Barry Margolin <bar...@genuity.net> writes:

> But if it's something produced computationally, the compiler can't reject
> it, since this value won't be known until run-time. The issue is *only*
> with a literal that can never evaluate to procedures being put in the
> procedure position of an expression. It had to come from somewhere, and
> whoever put it there should have known that it was going to end up in a
> procedure position eventually, so should have put something that is
> syntactically valid there.

And so he did. The elements of a combination can be any expressions,
according to the syntax.

By "the syntax", of course, I mean "the syntax as has always been
specified in every Scheme standard produced".

Thomas Bushnell, BSG

unread,
May 15, 2002, 3:45:37 PM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> Yes. I know the standard. All I said was that it comes up short
> according to your "exploratory programming" claim. To me, it is ok to
> reject this code, and so is rejecting (0 ...).

Except that by rejecting (0 ...) you fail to compile valid Scheme
programs. If you don't care beans about compiling valid Scheme
programs, why not write a C compiler? Of course, then you'd have to
implement *that* standard.

Thomas Bushnell, BSG

unread,
May 15, 2002, 3:55:51 PM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> For the last time: I *know* the RnRS syntax, and I *deliberately*
> implemented a recognizer for a slightly different syntax in my
> compiler -- for the purpose of statically rejecting certain programs
> that do not have any conceivable purpose (other than providing fuel
> for netnews flamewars :-).

Since the language you implement is deliberately not Scheme (according
to *any* version of the language), why do you call it Scheme? Why do
you think your comments have been the least bit on-topic here? Al
pointed out an unexpected consequence of the Scheme programming
language.

To natter on about some *other* language--not Scheme, by deliberate
design--is really quite silly. I mean, C prohibits the following, and
even *requires* a compile-time error:

foo (char (*a)()) {0(a());}

even if the call to the actual parameter happens to exit "foo"
nonlocally.

But that's C, not Scheme. C has a different syntax; we all know that;
and what the syntax of C is in this respect is irrelevant to Scheme.

Since your compiler is *deliberately* not Scheme, why'd you bring it
up at all???

Thomas

Al Petrofsky

unread,
May 15, 2002, 4:19:09 PM5/15/02
to
Barry Margolin <bar...@genuity.net> writes:

> In article <87lmal1...@radish.petrofsky.org>,
> Al Petrofsky <a...@petrofsky.org> wrote:
> >Barry Margolin <bar...@genuity.net> writes:
> >> I'll happily use a compiler that refuses to compile the function.
> >> ... While I don't like the compiler to tie my hands, I don't mind it
> >> assisting me by pointing out bogus code.
> >
> >I'm a bit confused by you making a distinction between hand-tying and
> >assistance, but then placing both rejections and warnings in the
> >latter category.
>
> Hand-tying is when a language or implementation prevents you from doing
> useful things (or makes it difficult to do them) because the language
> designer or implementor didn't think they were good ideas.

If the implementor is refraining from judging whether a method is a
"good idea", shouldn't that include, by the same principle, refraining
from judging whether it is "useful"?

> Rejecting code that makes no sense doesn't fall into that category.

You keep using "makes no sense" in a way I don't understand. The
expression (lambda 7) makes no sense. However, the language
specification ascribes a definite meaning, or sense, to the expression
in the subject line. How is declaring it nonsensical, which you find
a valid reason to reject it, distinct from "not thinking it's a good
idea", which you say is an invalid reason?

-al

Matthias Blume

unread,
May 15, 2002, 4:22:37 PM5/15/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > Yes. I know the standard. All I said was that it comes up short
> > according to your "exploratory programming" claim. To me, it is ok to
> > reject this code, and so is rejecting (0 ...).
>
> Except that by rejecting (0 ...) you fail to compile valid Scheme
> programs.

They are "valid", all right, but they are not useful.

> If you don't care beans about compiling valid Scheme
> programs, why not write a C compiler?

I (used to) care about compiling useful Scheme programs with
"useful" \subset "valid" and pretty clearly "useful" \neq "valid".
In other words, I don't give a damn about so-called "valid" programs
that obviously cannot have any purpose.

--
-Matthias

Matthias Blume

unread,
May 15, 2002, 4:19:45 PM5/15/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

But all of this is completely outside the C standard. In other words,
you are invoking implementation-specific behavior. I do not see how
this proves or disproves (or has anything to do with) the question we
are discussing.

--
-Matthias

Scott G. Miller

unread,
May 15, 2002, 4:37:30 PM5/15/02
to
> I (used to) care about compiling useful Scheme programs with
> "useful" \subset "valid" and pretty clearly "useful" \neq "valid".
> In other words, I don't give a damn about so-called "valid" programs
> that obviously cannot have any purpose.
>

So its your compiler's (and by extension your) job to tell the
programmer what are and are not "useful" programs?

Thomas Bushnell, BSG

unread,
May 15, 2002, 4:39:02 PM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> But all of this is completely outside the C standard. In other words,
> you are invoking implementation-specific behavior. I do not see how
> this proves or disproves (or has anything to do with) the question we
> are discussing.

Um, no. The standard is quite explicit about the behavior of
divide-by-zero, and that attempting it with volatile ints *must* have
certain well-specified consequences.

Thomas

ozan s yigit

unread,
May 15, 2002, 4:16:55 PM5/15/02
to
Al Petrofsky <a...@petrofsky.org> writes:

> We've all stated our agreement that it is helpful for the compiler to
> detect this situation. The question is whether it should reject the
> code or issue a warning.

given certain run-time failure, the nature of the warning may be
important too:

sic22: line 42: ...
sic22: WARNING: the generated code for this block will fail when
run. It is generated for academic interest only. Do NOT attempt to
use this code for any purpose where well-being of others may depend
on the results. if you do not understand this message, please refer
to your lawyer.

oz
--
where is some sand when your head needs it? -- larry wall

Matthias Blume

unread,
May 15, 2002, 4:33:14 PM5/15/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

Ok, I already knew that you are arguing for the argument's sake.
Thanks for making this crystal-clear.

--
-Matthias

Barry Margolin

unread,
May 15, 2002, 4:58:51 PM5/15/02
to
In article <87g00t5...@becket.becket.net>,

Thomas Bushnell, BSG <tb+u...@becket.net> wrote:
>Barry Margolin <bar...@genuity.net> writes:
>
>> But if it's something produced computationally, the compiler can't reject
>> it, since this value won't be known until run-time. The issue is *only*
>> with a literal that can never evaluate to procedures being put in the
>> procedure position of an expression. It had to come from somewhere, and
>> whoever put it there should have known that it was going to end up in a
>> procedure position eventually, so should have put something that is
>> syntactically valid there.
>
>And so he did. The elements of a combination can be any expressions,
>according to the syntax.

So? It makes no sense to provide a literal expression that can *never*
evaluate to a procedure as a parameter that you know will be used as a
procedure.

Matthias Blume

unread,
May 15, 2002, 4:55:39 PM5/15/02
to

No. The compiler rejects only those programs that contain some
construct which is *obviously* not useful. :-)

Actually, I am almost serious about this. You can try to disprove me
(but I will probably no longer answer on this thread regardless of
what you come up with, it is getting tiring and pointless) by
providing an explanation for why (0 ...) should be useful in any
Scheme program.

--
-Matthias

Barry Margolin

unread,
May 15, 2002, 5:21:49 PM5/15/02
to
In article <877km51...@radish.petrofsky.org>,

Al Petrofsky <a...@petrofsky.org> wrote:
>Barry Margolin <bar...@genuity.net> writes:
>
>> In article <87lmal1...@radish.petrofsky.org>,
>> Al Petrofsky <a...@petrofsky.org> wrote:
>> >Barry Margolin <bar...@genuity.net> writes:
>> >> I'll happily use a compiler that refuses to compile the function.
>> >> ... While I don't like the compiler to tie my hands, I don't mind it
>> >> assisting me by pointing out bogus code.
>> >
>> >I'm a bit confused by you making a distinction between hand-tying and
>> >assistance, but then placing both rejections and warnings in the
>> >latter category.
>>
>> Hand-tying is when a language or implementation prevents you from doing
>> useful things (or makes it difficult to do them) because the language
>> designer or implementor didn't think they were good ideas.
>
>If the implementor is refraining from judging whether a method is a
>"good idea", shouldn't that include, by the same principle, refraining
>from judging whether it is "useful"?

0 can't be used as a function, so what possibly use could there be in
putting it in the function position of an expression?

The kinds of things that fall into the "good idea" vs. "bad idea" judgement
area are things like static typing. The designers of Ada felt that this is
a critical requirement, but the designers of Lisp and Scheme felt
otherwise.

My guess about why the section of the Scheme specification is written the
way it is is because it simplifies the description and implementation. It
wasn't felt necessary to *require* implementations to check whether the
object in the function position is really a procedure until it's actually
called, and then the semantics of procedure calls indicate that it's an
error. I find it difficult to believe that they really wanted to enable
silly code like (0 <expression>); they just didn't feel the need to
explicitly forbid it.

>> Rejecting code that makes no sense doesn't fall into that category.
>
>You keep using "makes no sense" in a way I don't understand. The
>expression (lambda 7) makes no sense. However, the language
>specification ascribes a definite meaning, or sense, to the expression
>in the subject line. How is declaring it nonsensical, which you find
>a valid reason to reject it, distinct from "not thinking it's a good
>idea", which you say is an invalid reason?

Because I can't think of any possible reason how (0 <expression>) could be
useful, regardless of whether <expression> returns or not. If it occurs, I
suspect that at least 99% of the time it is unintended, and at least 75% of
the times when it's intended it's just because someone is testing for
conformity with this corner of the standard. And of the remaining 0.25%,
there's almost certainly a better way that it could be written
(e.g. (cant-get-here <expression>)).

Matthias Blume

unread,
May 15, 2002, 5:25:55 PM5/15/02
to
tb+u...@becket.net (Thomas Bushnell, BSG) writes:

Could you, please, give some hints on what this behavior has to be. I
do not have access to more recent C standards. Both the ANSI C
standard from 1989 and H&S are quite explicit in that division by zero
invokes undefined behavior. (No word about volatile in this context.)

ANSI X3.159-1989, sec. 3.3.5, lines 23 and 24:

[ ... ] In both operations, if the value of the second operand is
zero, the behavior is undefined.

H&S, fourth edition, p. 202 (sec. 7.6.1):

[ ... ] The consequences of division by zero--integer or floating
point--are undefined.

--
-Matthias

Scott G. Miller

unread,
May 15, 2002, 5:54:20 PM5/15/02
to

There may be good examples in the realm of dynamically generated
programming, but lets ignore that. You stated earlier that if the user
wished an error to be signaled at runtime, he could use:

(error ...)

However, error is not part of the Scheme standard. If the user wants an
error raised and the computation terminated, and wants his code to be
runnable on any standard-compliant Scheme system, he is then forced to
find a piece of code that is syntactically valid (which [1] does not
produce a compile error), but will cause an error at runtime.

Another obvious choice may be (car '()), which I frequently use to raise
an error in test code. By your logic, (car '()) is obviously 'not
useful', and thus should be caught at compile time. But I as a
programmer intended an error to be raised at runtime. Nevermind a
construct that is syntactically valid,would raise an error at runtime,
but doesn't because of an alternate code path.

If you start deciding what is useful, no matter how obviously not useful
it seems, you must assume that you are interfering with a programmer's
desire. If you are trying to help the programmer spot errors, a warning
is sufficient.

Finally, if you aren't implementing Scheme (as specified by any of the
standards), thats your perogative, but you must state clearly that you
are writing a Scheme-like compiler that differs from the standard in a
way that prevents code that doesn't meet your definition of usefulness
from being written.

Scott

Thomas Bushnell, BSG

unread,
May 15, 2002, 6:15:25 PM5/15/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> Could you, please, give some hints on what this behavior has to be. I
> do not have access to more recent C standards. Both the ANSI C
> standard from 1989 and H&S are quite explicit in that division by zero
> invokes undefined behavior. (No word about volatile in this context.)

Quite right. It must do something undefined. (Similarly for
generating runtime errors in Scheme.) But it must be successfully
compiled.

Thomas Bushnell, BSG

unread,
May 15, 2002, 6:14:36 PM5/15/02
to
Barry Margolin <bar...@genuity.net> writes:

> So? It makes no sense to provide a literal expression that can *never*
> evaluate to a procedure as a parameter that you know will be used as a
> procedure.

It certainly does make sense if your goal is to either:

1) Generate an error at runtime, or
2) Express clearly to the reader of the program that the combination
will not be executed.

Thomas Bushnell, BSG

unread,
May 15, 2002, 6:46:06 PM5/15/02
to

Indeed, let's consider the case of writing a portable implementation
of `error', from SRFI 23.

The SRFI itself suggests the following:

(define (error reason . args)
(display "Error: ")
(display reason)
(for-each (lambda (arg)
(display " ")
(write arg))
args)
(newline)
(scheme-report-environment -1))

Let's factor this a little into:

(define (error reason . args)
(print-error-message reason args)
(abort))

(define (print-error-message reason args)
(display "Error: ")
(display reason)
(for-each (lambda (arg) (display " ") (write arg)) args)
(newline))

And now the question is, how to implement abort in a standard way?

SRFI 23's solution is:

(define (abort) (scheme-report-environment -1))

However, the standard does not require scheme-report-environment to
return an error here. And indeed, the SRFI only says "we hope that
this will signal an error". And a compiler writer might easily decide
that such arguments to scheme-report-environment are useless, and
refuse to compile the procedure at all.

So we must look for something that is guaranteed to be an error at
runtime (unlike scheme-report-environment). We are looking for
something syntactically valid but semantically erroneous--and
guaranteed by the standard to be an error. Candidate expressions are:

(0)
(car 6)
("Error!")

But under the reasoning expressed here by some, the compiler should
dump these entirely. How to generate a runtime error then?! We need
something that

1) The compiler cannot prove to be an error, and
2) Is guaranteed to actually produce an error.

It's not enough to replace the relevant constant in the sample
expressions with something programmatic. For example,

((- 1 1))

is the same as (0), and a compiler can easily prove the equivalence.
If it rejects (0), it might then reject ((- 1 1)). And that leaves us
with no way to write the abort procedure.

Suppose we try

(define (abort)
((generate-zero-unprovably)))

where we want something like

(define (generate-zero-unprovably)
0)

Except that we want to obfuscate the definition of
generate-zero-unprovably so that the compiler can't detect what its
value will be. Note that one way to tell, if generate-zero-unprovably
is a pure function, is just to run it. So it can't be pure; it will
have to depend on some external value in some way:

(define external-magic-value 0)
(define (generate-zero-unprovably)
external-magic-value)

This is sufficient, as long as we can make sure the compiler is unsure
whether external-magic-value might change. The only way to do that is
to have some other thread (or other mechanism) call the following
every now and then

(define (maybe-mutate-emv)
(if (do-something-hairy)
(set! external-magic-value +)
(set! external-magic-value 0)))

Note that this is horribly non-global. We need to call
maybe-mutate-emv "a bunch of times" before any possible call to
abort. Enough times that do-something-hairy has baffled the
compiler. I'm not sure there is a solution to that problem, but let's
suppose there is.

Ok, now we need a do-something-hairy which

1) Never returns true
2) The compiler cannot prove to return true.

Fortunately, thanks to the work of Gödel, we can define such a thing.

(define proof-number 0)
(define (do-something-hairy)
(set! proof-number (+ proof-number 1))
(is-a-proof-of? proof-number not-godel-sentence))

We need here a Gödel sentence that we know to be true, but which the
compiler is unable to prove true. I'm not sure that such a thing can
be constructed without knowledge of the particular compiler in
question.

I hope it is now clear why the following definition of abort is
better:

(define (abort) ("Program Abort"))

Thomas

Bob

unread,
May 15, 2002, 6:53:03 PM5/15/02
to
I for one am getting something (more than annoyance) out of this thread.
Also, for educational purposes, it is good to show students that (define foo
(lambda () (0 'anything))) does in fact create a closure, but (foo) is an
error. I think you might be wanting some other type of "useful", but this
is the only reason why (0 ...) has ever been useful to me.

Bob


"Matthias Blume" <matt...@shimizu-blume.com> wrote in message
news:fohel9t...@blume-pcmh.research.bell-labs.com...

Barry Margolin

unread,
May 15, 2002, 6:57:03 PM5/15/02
to
In article <87hel9o...@becket.becket.net>,

Thomas Bushnell, BSG <tb+u...@becket.net> wrote:

No, I don't think compilers are required to compile a program that it can
determine will invoke undefined behavior.

But I presume the situation you're talking about is a case where it *might*
divide by zero, but can't prove it, e.g.

if (something)
a/0;

This should presumably be compiled, and you just have to hope that
something is never true.

Personally, I would be OK with that code generating a warning. Rejecting
it is harder to justify, because the 0 could have come from a macro
expansion.

Now I expect that you're going to say that I previously rejected the
"automatically generated code" excuse for (0 (cont x)). The difference is
that in the above code, the macro still expands into a number, and that
macro is likely to be used in a number of numeric contexts, and it's likely
that 0 is valid in many of them. But I have a very hard time imagining
code where that 0 is coming from someplace that isn't required to provide a
procedure -- numbers and procedures are simply far less likely to be
acceptable in the same contexts than zero and non-zero numbers are.

I wish someone would show a real-world example of portable code that needs
this. So far it just seems like a totally academic discussion.

If I were an implementor I would probably accept that it's a bug. But when
I'm prioritizing which bugs to fix, it would be way down at the bottom of
my list. In fact, I would probably work on implementing new feature
requests ahead of fixing this bug. It seems like a bug that's extremely
unlikely to affect any actual code, and the workaround is trivial, so I
wouldn't lose sleep over it.

Thomas Bushnell, BSG

unread,
May 15, 2002, 7:13:33 PM5/15/02
to
Barry Margolin <bar...@genuity.net> writes:

> But I presume the situation you're talking about is a case where it *might*
> divide by zero, but can't prove it, e.g.
>
> if (something)
> a/0;
>
> This should presumably be compiled, and you just have to hope that
> something is never true.

In the world you suggest, the compiler is free to poke inside
something and try to prove it true, and if it succeeds, to thwart the
intentions of the code. As I explained in a separate message, it is
far from easy to defeat the compiler in this way if you are confronted
with a compiler designer who thinks he is free to avoid emitting
error-generating code anytime he can prove the error will occur.

> But I have a very hard time imagining code where that 0 is coming
> from someplace that isn't required to provide a procedure -- numbers
> and procedures are simply far less likely to be acceptable in the
> same contexts than zero and non-zero numbers are.

What you are saying here is that numbers and procedures are different
types by context. But that's Just Not True In Scheme. It may be a
very common--even a hugely common--convention, but it is Just Not
True. Scheme does *not* have typed variables, and it does *not* even
have typed contexts.

> I wish someone would show a real-world example of portable code that needs
> this. So far it just seems like a totally academic discussion.

Just gave one in a separate message: the attempt to define 'error'
portably.

Even so, you miss part of the point of Scheme greatly. It is not just
there to work with "real-world examples", but it is *also* designed to
be expressible by formal semantics in a clear and simple way. That is
a *very* important goal, which is totally defeated by such a change as
you suggest.

> If I were an implementor I would probably accept that it's a bug. But when
> I'm prioritizing which bugs to fix, it would be way down at the bottom of
> my list. In fact, I would probably work on implementing new feature
> requests ahead of fixing this bug. It seems like a bug that's extremely
> unlikely to affect any actual code, and the workaround is trivial, so I
> wouldn't lose sleep over it.

Nobody has been arguing for any particular allocation of time. There
is a huge difference between "ooh, weird bug, I'll add it to the list
of weird bugs" and "I deliberately wanted my compiler to have this
behavior".

Thomas

It is loading more messages.
0 new messages