summaryrefslogtreecommitdiff
path: root/s7.html
diff options
context:
space:
mode:
Diffstat (limited to 's7.html')
-rw-r--r--s7.html148
1 files changed, 131 insertions, 17 deletions
diff --git a/s7.html b/s7.html
index 5c2481b..080047d 100644
--- a/s7.html
+++ b/s7.html
@@ -151,7 +151,7 @@ s7.h, that want only to disappear into someone else's source tree. There are no
no run-time init files, and no configuration scripts.
It can be built as a stand-alone
interpreter (see <a href="#repl">below</a>). s7test.scm is a regression test for s7.
-A tarball is available: <a href="ftp://ccrma-ftp.stanford.edu/pub/Lisp/s7.tar.gz">s7 tarball</a>.
+A tarball is available: <a href="https://ccrma.stanford.edu/software/s7/s7.tar.gz">s7 tarball</a>.
</p>
<p>
@@ -310,6 +310,9 @@ Its argument is a string representing the desired number:
<em class="gray">1.12312312312312312312312312300000000009E0</em>
</pre>
+<p>Currently the gmp code is not completely integrated into s7's optimizer, so the gmp version
+of s7 is much slower than the ordinary version. I hope to fix this someday...
+</p>
<blockquote>
<div class="indented">
@@ -1237,7 +1240,7 @@ One long-winded way in s7 uses <a href="#unlet">unlet</a>:
<em class="gray">5</em>
</pre>
-<p>But this is hard to read, and it's not inconceivable that we might want all three
+<p>But this is hard to read, and we might want all three
values of a symbol, the start-up value, the definition-time value, and the
current value. The latter can be accessed with the bare symbol, the definition-time
value with unquote (','), and the start-up value with either unlet
@@ -1263,9 +1266,10 @@ or #_&lt;name&gt;. That is, #_+ is a reader macro for <code>(with-let (unlet) +
<em class="gray">"hiho"</em>
</pre>
-<p>#_&lt;name&gt; could be implemented via *#readers*:
+<blockquote>
+<div class="indented">
+<p>Conceptually, #_&lt;name&gt; could be implemented via *#readers*:
</p>
-
<pre class="indented">
(set! *#readers*
(cons (cons #\_ (lambda (str)
@@ -1273,6 +1277,19 @@ or #_&lt;name&gt;. That is, #_+ is a reader macro for <code>(with-let (unlet) +
(string-&gt;symbol (substring str 1)))))
*#readers*))
</pre>
+<p>but #\_ can't be set by *#readers*; otherwise someone could:
+</p>
+<pre class="indented">
+(set! *#readers* (list (cons #\_ (lambda (str) (string-&gt;symbol (substring str 1))))))
+</pre>
+<p>and now #_ provides no protection:
+</p>
+<pre>
+&gt; (let ((+ -)) (#_+ 1 2))
+<em class="gray">-1</em>
+</pre>
+</div>
+</blockquote>
<p>
So, now we have only the variable capture problem ('a' has been captured in the preceding examples).
@@ -1298,7 +1315,9 @@ national debt. gensym is the standard approach:
<em class="gray">13</em>
</pre>
-<p>But in s7, the simplest approach uses environments. You have complete
+<p>I think syntax-rules and its friends try to conjure up gensyms automatically, but
+the real problem is not name collisions, but unspecified environments.
+In s7 we have first-class environments, so you have complete
control over the environment at any point:
</p>
@@ -1398,6 +1417,61 @@ to be evaluated in its definition environment:
(mac-1 1))))
</pre>
+<blockquote>
+<div class="indented">
+<p>Here are some variations on "unless", inspired by the wikipedia hygienic macro page:
+</p>
+<pre>
+(define-macro (my-unless condition . body)
+ `(with-let (inlet (unlet) :condition ,condition) ; here unlet protects body (format below)
+ (if (not condition) (begin ,@body))))
+
+(let ((not (lambda (x) x))
+ (begin 32)
+ (if +)
+ (format abs))
+ (my-unless #t (format #t "This should not be printed!\n"))
+ (my-unless #f (format #t "This should be printed!\n")))
+
+(set! format abs)
+(let ((not (lambda (x) x)))
+ (my-unless #t (format #t "This should not be printed!\n"))
+ (my-unless #f (format #t "This should be printed!\n")))
+
+(define (user-defined-operator x) (not x))
+
+(define-macro (my-unless-1 condition . body)
+ `(with-let (inlet (unlet) :condition ,condition)
+ (if (user-defined-operator condition) (begin ,@body))))
+
+(let ((user-defined-operator (lambda (x) x)))
+ (my-unless-1 #t (format #t "This should not be printed!\n"))
+ (my-unless-1 #f (format #t "This should be printed!\n")))
+
+(define my-unless-2
+ (let ((op1 (lambda (x) (not x))))
+ (define-macro (_ condition . body)
+ `(with-let (inlet (unlet) (funclet my-unless-2) :condition ,condition)
+ ;; funclet above to get my-unless-2's version of op1
+ (if (op1 condition) (begin ,@body))))))
+
+(let ((op1 (lambda (x) x)))
+ (my-unless-2 #t (format #t "This should not be printed!\n"))
+ (my-unless-2 #f (format #t "This should be printed!\n")))
+
+(define my-unless-3
+ (let ((op1 (lambda (x) x)))
+ (define-macro (_ condition . body)
+ `(with-let (inlet (unlet) :condition ,condition :local-env (curlet))
+ ;; curlet to get run-time local version of op1
+ (if ((with-let local-env op1) condition) (begin ,@body))))))
+
+(let ((op1 (lambda (x) (not x))))
+ (my-unless-3 #t (format #t "This should not be printed!\n"))
+ (my-unless-3 #f (format #t "This should be printed!\n")))
+</pre>
+</div>
+</blockquote>
<!--
(define (tree-quote tree args)
@@ -1416,7 +1490,7 @@ to be evaluated in its definition environment:
`(define-macro ,name-and-args
(list 'with-let
(list 'inlet ,@(map (lambda (arg)
- (values (symbol->keyword arg) arg))
+ (values (symbol-&gt;keyword arg) arg))
args))
,@(tree-quote body args)))))
@@ -1427,6 +1501,47 @@ to be evaluated in its definition environment:
; (lambda (a b) (list 'with-let (list 'inlet :a a :b b) (list-values '+ 'a 'b)))
-->
+<blockquote>
+<div class="indented">
+<p>On the subject of *#readers*, say we have:
+</p>
+<pre>
+(set! *#readers* (list (cons #\o (lambda (str) 42)) ; #o... -&gt; 42
+ (cons #\x (lambda (str) 3)))) ; #x... -&gt; 3
+</pre>
+<p>Now we load a file with:
+</p>
+<pre>
+(define (oct) #o123)
+
+(let-temporarily ((*#readers* ()))
+ (eval (with-input-from-string "(define (hex) #x123)" read)))
+
+(define-constant old-readers *#readers*)
+(set! *#readers* ())
+
+(define (oct1) #o123)
+(define (hex1) #x123)
+
+(set! *#readers* old-readers)
+
+(define (oct2) #o123)
+(define (hex2) #x123)
+</pre>
+<p>Now we evaluate these functions, and get:
+</p>
+<pre>
+(oct): 42 ; oct is not read-time hygienic so #o123 -&gt; 42
+(oct1): 83 ; oct1 is protected by the top-level set, #o123 -&gt; 83
+(oct2): 42 ; same as oct
+(hex): 291 ; hex is protected by let-temporarily + read
+(hex1): 291 ; hex1 is like oct1
+(hex2): 3 ; hex2 is like oct
+</pre>
+
+</div>
+</blockquote>
+
<blockquote>
<div class="indented">
@@ -2510,7 +2625,7 @@ Environments are first class (and applicable) objects in s7.
(<em class=def id="unlet">unlet</em>) a let with any built-in functions that do not have their original value
(<em class=def id="letref">let-ref</em> env sym) get value of sym in env, same as (env sym)
-(<em class=def id="letset">let-set!</em> env sym val) set value of sym in val to val, same as (set! (env sym) val)
+(<em class=def id="letset">let-set!</em> env sym val) set value of sym in env to val, same as (set! (env sym) val)
(<em class=def id="inlet">inlet</em> . bindings) make a new environment with the given bindings
(<em class=def id="sublet">sublet</em> env . bindings) same as inlet, but the new environment is local to env
@@ -2759,7 +2874,7 @@ that those values can be clobbered).
</p>
<p>
-<code>(fill! lt &lt;undefined&gt;)</code> removes all bindings from the let lt.
+<code>(fill! lt #&lt;undefined&gt;)</code> removes all bindings from the let lt.
</p>
<blockquote>
@@ -4078,9 +4193,8 @@ form containing #&lt;eof&gt;, just as with any other constant. If it hits the e
the input while reading a form, it raises an error (e.g. "missing close paren").
If it encounters
#&lt;eof&gt; all by itself at the top level (this never happens),
-it returns that #&lt;eof&gt;. Consider it a feature! If you want a top level
-#&lt;eof&gt; without stopping read, either quote it, or <code>(define *eof* #&lt;eof&gt;)</code> and use *eof* in the
-source: read will return the symbol *eof*. Built-in #&lt;eof&gt; has lots of
+it returns that #&lt;eof&gt;.
+Built-in #&lt;eof&gt; has lots of
uses, and as far as I can see, no drawbacks. For example,
it is very common to call
read (or one of its friends) in a loop which first checks for #&lt;eof&gt;, then falls into
@@ -5595,8 +5709,8 @@ provided? should be feature? or *features* should be *provisions*.
list-ref, list-set!, and list-tail actually only apply to pairs.
let-temporarily should be templet, or maybe set-temporarily.
Finally, the CL-inspired "log*" names such as logand look very old-fashioned. Standard scheme opts
-for the name "bitwise*"; why not "integerwise" or "bytevectorwise"? The "wise" business is just noise.
-Perhaps <code>(define & logand) (define | logior) (define ~ lognot)</code> and so on, but ^ for logxor
+for the name "bitwise*"; why not "integerwise" or "bytevectorwise"? The "wise" business is just noise; are they thinking of "The Hobbit"?
+<code>(define & logand) (define | logior) (define ~ lognot)</code>, but ^ for logxor
(as in C) is not ideal; ^ should be expt.
</p>
@@ -5654,7 +5768,7 @@ Better ideas are always welcome!
<li>pi
<li>*stdin* *stdout* *stderr*
<li>*s7*
-<li>nan.0 +nan.0 -nan.0 inf.0 +inf.0 -inf.0 (what crappy names! nan.0 is an inexact integer that is not a number?)
+<li>+nan.0 -nan.0 +inf.0 -inf.0 (what crappy names! +nan.0 is an inexact integer that is not a number?)
<li>*unbound-variable-hook* *missing-close-paren-hook* *load-hook* *error-hook* *read-error-hook* *rootlet-redefinition-hook*
</ul>
@@ -6148,14 +6262,14 @@ so misleading that I feel guilty about it):
(for-each do-loop (list 1000 1000000 10000000))
</pre>
-<p>In s7, that takes 0.1 seconds on my home machine. In tinyScheme, from
+<p>In s7, that takes 0.09 seconds on my home machine. In tinyScheme, from
whence we sprang, it takes 85 seconds. In the chicken interpreter, 5.3
seconds, and after compilation (using -O2) of the chicken compiler output,
0.75 seconds. So, s7 is comparable to chicken in speed, even though chicken
is compiling to C. I think Guile 2.0.9 takes about 1 second.
The equivalent in CL:
clisp interpreted 9.3 seconds, compiled 0.85 seconds; sbcl 0.21 seconds.
-Similarly, s7 computes (fib 40) in 1.5 seconds, approximately the same as sbcl.
+Similarly, s7 computes (fib 40) in 0.8 seconds, approximately the same as sbcl.
Guile 2.2.3 takes 7 seconds.
</p>
@@ -6221,7 +6335,7 @@ Here are the diffs for the bench script:
<p>
I call the standalone version of s7 "repl", so its path
is /home/bil/motif-snd/repl. To build repl, get s7.tar.gz
-from ftp://ccrma-ftp.stanford.edu/pub/Lisp/s7.tar.gz,
+from https://ccrma.stanford.edu/software/s7/s7.tar.gz,
add the empty file mus-config.h to the tarball's contents,
then (in Linux):
</p>