[gambit-list] Fatal crash
Marc Feeley
feeley at iro.umontreal.ca
Mon Apr 14 22:53:25 EDT 2008
On 14-Apr-08, at 3:58 PM, Derek Peschel wrote:
> On Mon, Apr 14, 2008 at 02:03:37PM -0400, Joel J. Adamson wrote:
>> Is there somewhere I can read more about this?
>
> The manual doesn't mention type safety or the namespace feature
> (which the ## prefix is one instance of) but the Wiki has a page
> on them. ## means "internal" more than "unsafe". Many of the
> internal procedures are unsafe, though, and all unsafe procedures
> are supposed to be internal.
>
> http://dynamo.iro.umontreal.ca/~gambit/wiki/index.php/Namespaces
>
> Comments from new readers might improve the page. That's why I'm
> posting to the list.
Here's a short explanation of the ## prefix.
Gambit is a "Scheme in Scheme", meaning that most of the code
(including the Gambit compiler, the Gambit interpreter and the Gambit
runtime system and libraries) is written in Scheme and compiled with
the Gambit compiler. OK, so how does this work? Take something
simple like the car procedure. How can car be implemented in Scheme?
It obviously can't be implemented as:
(define (car x)
(car x))
Since this is an infinite recursion. Gambit's implementation of the
car procedure corresponds roughly to this:
(define (car x)
(declare (extended-bindings))
(if (pair? x)
(##car x)
(error "car expects a pair!")))
The ##car procedure which is called by car is a version of car which
assumes that the parameter is a pair. In other words ##car is simply
an indirection (with offset). Thanks to the extended-bindings
declaration the compiler knows that the reference to the ##car
variable truly refers to the ##car procedure, so the compiler can
inline this operation. The resulting C code for the body of car is
roughly the following:
/* x is a ___SCMOBJ, which is of type int */
if (___PAIRP(x))
result = ___CAR(x);
else
error(...);
where ___PAIRP and ___CAR are defined in include/gambit.h as something
like:
#define ___tPAIR 3
#define ___PAIRP(x) (((x) & 3) == ___tPAIR)
#define ___CAR(x) (___SCMOBJ*)((x) - ___tPAIR)[1]
#define ___CDR(x) (___SCMOBJ*)((x) - ___tPAIR)[2]
The original purpose of the ## prefix is to give a non-conflicting
name for the primitive operations, such as ##cons, ##car, ##pair?,
which are needed to implement more complex procedures such as append
and write. Note that the ## prefix is illegal in Scheme, so these
names (which are accepted by the Gambit reader) will never conflict
with variable names defined by portable Scheme code. This is fairly
common practice, for instance MzScheme uses the #% prefix for these
"hidden" names.
The ## prefix has also been used to "keep out of the user's way" some
of the runtime's utility procedures which are not meant to be called
directly by the user. For performance reasons, these procedures
normally do not check the type of their parameters, and they only call
other ## procedures. As a general rule for most non-## procedures
provided by the runtime system there is a corresponding ## procedure
which does the same operation without type checks, and sometimes with
slightly different parameters. For example the ##append procedure
takes exactly two parameters and it does not require that the first
parameter is a proper list (any non-pair object is treated as the
empty list), so (##append 11 '(22 . 33)) => (22 . 33). This makes it
easy to enforce one of Scheme's requirements that the redefinition of
a predefined global variable does not change the behaviour of
procedures which would seem to have to call them. For example the
naive (and incorrect) definition of list-ref is:
(define (list-ref lst i)
(if (= i 0)
(car lst)
(list-ref lst (- i 1))))
It is incorrect because doing (set! = <) or (set! car cdr) would
change the behaviour of list-ref. Instead, list-ref is implemented
roughly as
(define (list-ref lst i)
(##list-ref lst i))
(define (##list-ref lst i)
(declare (extended-bindings))
(if (##= i 0)
(##car lst)
(##list-ref lst (##- i 1))))
That way a mutation of any of the global variables =, car, -, list-ref
will not change the behaviour of the list-ref procedure:
(let ((lr list-ref))
(set! = <)
(set! list-ref vector-ref)
(lr '(11 22 33) 1)) => 22
So as a general rule:
1) ## procedures are unsafe (they assume the parameters are of the
appropriate type)
2) simple ## procedures are inlinable by the compiler (##car, ##vector-
ref, ##fixnum.+, etc)
3) if the runtime system provides procedure P, then ##P does the same
operation without checking the parameters
Marc
More information about the Gambit-list
mailing list