Thanks for the explanation. I suspected it was for efficiency reasons,
but as I've never implemented a "real" scheme, I don't know the trade
off. I wonder how bad it is. Way back, they invented lisp. Then they
said it was too slow for real stuff. Now they say other languages are
too weak for real stuff, and lisp's relatively little slowness is made
up for by its power. And here we are, saying a powerful feature would
slow it down!
Is it 2x, 3x, 4x, 10x, infinityx slower to implement some kind of lvalue
system? And wouldn't that system be necessary only in code that uses
the mutation?
On 06/09/2013 08:18 AM, Carl Eastlund wrote:
Sean,
Not every Scheme uses an interpreter with an eval function as its
primary method of execution, or even at all. Racket uses a bytecode
interpreter and a JIT native-code compiler; the eval function simply
triggers compilation to bytecode. These give a great deal more
efficiency than running via eval, and supporting multiple modes of
execution would be significantly more expensive. Evaluating to values
by default, rather than to addresses, also gives the compiler a great
deal of flexibility. It doesn't need to keep track of the addresses
where it found things and refer to them there in case they are about
to be mutated; once they have been "found" via evaluation, they can be
copied to register and the original address can be forgotten, if
that's most expedient. I'm not a compiler implementer myself, so I'm
sure others can probably give more specific details. In the meantime,
I hope this explanation is helpful.
Carl Eastlund
On Thu, Jun 6, 2013 at 4:12 PM, Sean Kanaley <skana...@gmail.com
<mailto:skana...@gmail.com>> wrote:
Hello all,
I was curious why Scheme and now Racket does not inherently
support a generic set!. I found an SRFI
http://srfi.schemers.org/srfi-17/srfi-17.html that suggests a
generic method solution requiring a lookup for the "real" setter
(and so needing a special setter for every data type. What is the
disadvantage of simply changing eval to take a "fetch?" parameter
that decides whether to ultimately resolve addresses to values?
Then set! is evaluated with this as #f and can operate on whatever
that address is. I have implemented this in a toy interpreter
with the bare minimum of forms plus vectors to test. The
vector-ref type of function gets applied as usual to the vector
and index arguments, except that if it's within a set! as the left
argument where the fetch? is #f and the final fetching of the
address given by vector-ref never happens.
Here's the critical pieces:
1. setting
"update" is what changes the store
set! is of course a clause within eval
the last parameter to eval in the first line says don't fetch
[(set! addr-x x) (match-let* ([(v*s addr s) (eval addr-x e s #f)]
[(v*s v s) (eval x e s)])
(update addr v s))]
2. evaluating symbols (another clause)
the symbol only returns its address with fetching off
[(sym y) (let* ([addr (lookup y e)]
[val (if fetch? (fetch addr s) addr)])
(v*s val s))]
3. the "built-in" (part of environment) vector-ref called vec@
"fetch?" will be false if (vec@ ...) is the first argument to set!
"a" is the base address of the vector
(define (vec@-f e s fetch? v i)
...unimportant stuff...
(let ([val (if fetch? (fetch (+ a i) s) (+ a i))])
(v*s val s)))))
So long as all built-in types have this conditional fetcher, then
every user type built on top of them won't need a special setter.
And since this would work just as well for inc! types funtions,
from now on
(vector-set! v i (add1 (vector-ref v i))
is
(inc! (vec@ v i))
I assume this has already been thought of and therefore discarded,
but why?
____________________
Racket Users list:
http://lists.racket-lang.org/users
____________________
Racket Users list:
http://lists.racket-lang.org/users