Re: Απ: Re: Απ: Re: Customization of evaluation for a specific custom type of object

Vassilis Radis <radisb@xxxxxxxxx> writes:

You are correct that persistence libraries might do what I need in a
generic sense. But in order to actually gain something from this
attempt of mine I would like to insist:

Why is it conceptually wrong to think of intercepting symbol
evaluation and just before returning the resulting value, examine it,
and if it is of type REFERENCE, replace the returning value by the
result of evaluating (fetch)?

Where is the conceptual mistake?

You changed the target.

First it was the object evaluation. I explained that it's not a
worthwhile goal, because lisp objects are not usually evaluated.

The only occurences where a (non symbol and non cons) lisp object is
evaluated, are:

- numerical literals, eg.: 42
- vector or array literals, eg.: #(1 2 3) #2A((1 2) (3 4))
- string literal, eg.: "abc"
- very rarely, structure literals: #S(point :x 2 :y 3)

other literals are usually quoted, and therefore not evaluated, or are
not present in lisp sources.

Actually, lisp objects of non standard types may be inserted in lisp
sources, either by using #., or thru a macro generating the source, but
then you must provide a load-form (by defining a make-load-form method
on the class of the object) to allow this code to be compiled and saved
in a fasl file. So there's some added complexity, that is not often
worth it; better just avoid random objects in source code.

Now it's symbol evaluation. I explained the difficulties and provided
by the standard solution: use of symbol macros. So you don't really
intercept the evalution of existing variables, but you define a symbol
macrolet that shadows the existing variables, and that the standard
mechanism specified and implemented into EVAL, COMPILE and COMPILE-FILE,
substitute by the given expansion.

Also I took a look in the source code of eval in sbcl
(src/code/eval.lisp) and basically it is a big case that essentialy
is structured like this:

(typecase exp
(ecase (info :variable :kind exp)
((:special :global :constant :unknown)
(symbol-value exp))
(%simple-eval original-exp lexenv))))
(... ALOT OF CODE...))

which means that there is point in trying to go after my second
version question. Conceptually isnt it like injecting in the above
code a conditional statement on the results of (symbol-value exp) or
(%simple-eval original-exp lexenv) that will check for the type and
call fetch?

The standard hooks provided by Common Lisp to change the behavior of
read, load, eval, compile and compile-file are:

- reader macros,
- macros,
- symbol macros,
- compiler macros.

If you want to change those functions, just write your own. See chapter
4 of SICL.

SICP = Structure and Interpretation of Computer Programs

[Note: the above code is missing the processing of symbol macros. I
guess that's because sbcl does it before calling the function you gave
the extract, notably given that sbcl compiles everything. ccl eval goes
to cheap-eval which calls cheap-eval-in-environment which contains:

(cond ((symbolp form)
(multiple-value-bind (expansion win) (cheap-eval-macroexpand-1 form env)
(if win
(cheap-eval-in-environment expansion env)
(let* ((defenv (definition-environment env))
(constant (if defenv (assq form (defenv.constants defenv))))
(constval (%cdr constant)))
(if constant
(if (neq (%unbound-marker-8) constval)
(error "Can't determine value of constant symbol ~s" form))
(if (constant-symbol-p form)
(%sym-global-value form)
(symbol-value form)))))))

where you can see that the form (the symbol) is first macro expanded,
and if it was a symbol macro, the macro expansion succeeds, and the
expansion is evaluated instead.

If you modified an eval to allow for the optional, user specified
processing of a symbol reference, you would get the same code as the
processing of symbol macros. It is already there, jus use it!]

__Pascal Bourguignon__
A bad day in () is better than a good day in {}.