diff options
Diffstat (limited to 's7.html')
-rw-r--r-- | s7.html | 148 |
1 files changed, 131 insertions, 17 deletions
@@ -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 #_<name>. That is, #_+ is a reader macro for <code>(with-let (unlet) + <em class="gray">"hiho"</em> </pre> -<p>#_<name> could be implemented via *#readers*: +<blockquote> +<div class="indented"> +<p>Conceptually, #_<name> could be implemented via *#readers*: </p> - <pre class="indented"> (set! *#readers* (cons (cons #\_ (lambda (str) @@ -1273,6 +1277,19 @@ or #_<name>. That is, #_+ is a reader macro for <code>(with-let (unlet) + (string->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->symbol (substring str 1)))))) +</pre> +<p>and now #_ provides no protection: +</p> +<pre> +> (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->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... -> 42 + (cons #\x (lambda (str) 3)))) ; #x... -> 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 -> 42 +(oct1): 83 ; oct1 is protected by the top-level set, #o123 -> 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 <undefined>)</code> removes all bindings from the let lt. +<code>(fill! lt #<undefined>)</code> removes all bindings from the let lt. </p> <blockquote> @@ -4078,9 +4193,8 @@ form containing #<eof>, 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 #<eof> all by itself at the top level (this never happens), -it returns that #<eof>. Consider it a feature! If you want a top level -#<eof> without stopping read, either quote it, or <code>(define *eof* #<eof>)</code> and use *eof* in the -source: read will return the symbol *eof*. Built-in #<eof> has lots of +it returns that #<eof>. +Built-in #<eof> 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 #<eof>, 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> |