Countering the counter-proposal oleg@xxxxxx (04 Oct 1999 20:52 UTC)
Re: Countering the counter-proposal Richard Kelsey (04 Oct 1999 23:37 UTC)

Countering the counter-proposal oleg@xxxxxx 04 Oct 1999 20:50 UTC

> Using `#,' in a program has two effects: it causes side effects to
> occur at read time and it ensures that a form is evaluated only once.
> The first has questionable utility and semantics.  The second is
> already available in R5RS as `delay' and `force'.

I have come across a counter-example to the last statement.

do ((i 0 (+ 1 i))) ((>= i 5))
    (let ((b (list (force (delay (begin (display "in form\n") 1 ))))))
      (display b) (newline)))
===>
in form
(1)
in form
(1)
in form
(1)
in form
(1)
in form
(1)

(include "myenv.scm")
(include "read-apply.scm")
(define-reader-ctor 'form (lambda () (display "in form\n") 1))

(do ((i 0 (+ 1 i))) ((>= i 5))
   (let ((b (list #,(form))))
      (display b) (newline)))
===>
in form
(1)
(1)
(1)
(1)
(1)

This shows that #,(ctor) has quite a different semantics than (force
(delay (ctor)). The latter can be evaluated several times if occurs
within a special form. The #,(ctor) expression is _really_ evaluated
only once - when it is read. This counter-example appears to
invalidate the counter-proposal and its discussion.

	It is instructive however to look into differences between
#,(ctor) and (force (delay (ctor)) further. The former evaluates at
read-time. By the time compiler/interpreter comes across #,(ctor), the
latter will be substituted by a _constant_ expression. A special form
will never see  #,(ctor) as it is -- it will see a constant. In
contrast, (force (delay (form)) within a special form will appear as
an expression, which cannot be tested at macro-expansion time, is not
eligible for const-folding, etc. Sergei Egorov was right that "It's a
pity that this [counter-proposal] form won't work inside
quoted constants, though." I should add that the counter-proposal
"will not work" within any syntax form (quote being one of them).

> I could get the same effect in vanilla R5RS, with much more power
> and flexibility, by using EVAL and an extensible reader:
>   (define my-reader (make-reader ...))
>   (eval (my-reader filename) some-environment)

I would like to dispute this statement as well. The first problem is
which 'some-environment' do you pass to eval? If it is an
(scheme-report-environment 5), a form read from the 'filename' may not
refer to procedures other than those in R5RS. Even if the parent
program defines a custom constructor for uniform vectors (per SRFI-4),
the 'filename' may not refer to it as it is not a part of R5RS. If the
'some-environment' stands for an interaction-environment the parent
then program tacitly permits the 'filename' to apply any function
defined in the parent program -- even such dangerous functions as
OS:system or OS:unlink if the current interaction-environment happens
to define them. You really don't want to do that. In any case this is
an "all-or-nothing" proposition. SRFI-10 devotes a special paragraph
to these issues (see a "Comparisons" subsection). Note R5RS does not
define any function to manipulate 'eval-environment's.

	Another problem with the eval-solution above is that it
contains "eval". SRFI-10 does not require a Scheme reader to perform
an evaluation of arbitrary expressions. SRFI-10 merely specifies that
the reader should be able to locate a constructor-procedure and apply
it to arguments. BTW, nowhere does SRFI-10 tell that the constructor
is a Scheme function; it can be written in any language as long as the
reader is able to invoke it passing appropriate parameters. For
example, one can write a Scheme->foo _compiler_ in ML and implement
reader-constructors as ML functions.

	The biggest problem with the above eval-solution  is that it
assumes that the form read from a file is an expression to be
evaluated. Suppose the file contains a string
	"(1 2 #,(f32 1.0 2.0))"
How can you pass the result of reading of this string to eval? All the
counter-example discussion tacitly assumes that the form being read is
a code form. But one can use a 'read' procedure to read arbitrary
data. For example, one can use read to load "persistent" data; or to
read information sent over communication channels. It has to be
stressed that not every Scheme object has a readable representation
(structures and records spring to mind). Furthermore, not every R5RS
object can be written (and then reloaded): for example, cyclical lists
and other data structures with circular dependencies, data structures
with large caches or memoization tables, etc. SRFI-10 proposes a way
to define a custom loader -- the only extension to the Scheme reader
that will be necessary to solve an immense number of problems.