guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Newbie question: bind a variable on the fly


From: Marius Vollmer
Subject: Re: Newbie question: bind a variable on the fly
Date: Sat, 10 Jun 2006 16:08:09 +0300
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.0.50 (gnu/linux)

Vincent De Groote <address@hidden> writes:

> Is there a way to catch an "unbound-variable" exception, bind the
> variable on the fly, and continue execution as if the exception didn't
> occurs ?

No, unfortunately not (but read on below for an alternative solution
that doesn't use exceptions).  Guile's exception system does not offer
'restarts' as Common Lisp does, for example.  That is, after an error
has been signalled in a computation, there is no way to resume the
computation after handling the error.

> I'd like to catch this exception in a c function: the exception
> context should be available, to retrieve the variable or function
> name.  This handler will lookup the value in a relational database.

You can create a special module that will do the database lookups when
variables are looked up.  This requires using using the low-level
module API, tho, which is unfortunately not that well supported at the
moment.  It definitely is not 'newbie' territory.

If that didn't scare you away successfully, here is an example of how
you might do it:

    (define (lazy-binder mod sym def?)
      (pk 'lazy sym def?)
      (let ((var (case sym
                   ((foo)
                    (make-variable 1))
                   ((bar)
                    (make-variable 2))
                   (else
                    #f))))
        (if var
            (module-add! mod sym var))
        var))

    ;; This otherwise useless call will memoize the 'failure-path' of the
    ;; lazy-binder and prevent infinite loops when it is used to lookup
    ;; the variables it uses itself.  This should not really happen
    ;; because the lazy-binder is used last (see below), but Guile also
    ;; looks up syntactic keywords like 'else' and only treats them as
    ;; keywords when they are not defined.  Thus, lazy-binder must be able
    ;; to return false for 'else' without causing 'else' to be looked
    ;; up...
    ;;
    (lazy-binder #f #f #f)

    (define module (make-module 31 '() lazy-binder))

    (set-module-uses! (current-module)
                      (append (module-uses (current-module))
                              (list module)))

    (pk (+ foo bar))

    (set! foo (1+ foo))

    (pk (+ foo bar))

The longish comment is another warning that crazy things might happen
when one messes with variable lookup.

Also, something that is probably important for your application is
that the module system (and the Guile evaluator itself) only allows
you too control the lookup of variables; once a variable has been
found, it is treated as any other variable and you don't get to run
code of your own each time it is accessed.  Thus, if you want to run a
SQL query each time a variable is read, or want to update the database
when the variable is set, you are out of luck, unfortunately.

If you want that, you might want to look at a more formal approach
using explicit declaration of which identifiers are supposed to refer
to the database.  Like

    (with-db-vars (foo bar)
      (set! (foo) (+ (foo) (bar))))

which could expand into something like

    (let ((foo (make-db-var 'foo))
          (bar (make-db-var 'bar)))
      (set! (foo) (+ (foo) (bar))))

using

    (define (make-db-var sym)
      (make-procedure-with-setter 
        (lambda () (db-ref 'foo))
        (lambda (val) (db-set! 'foo val))))

You could also look into "symbol macros" which allow you to do the
same thing without having to surround every use of 'foo' with
parentheses.

-- 
GPG: D5D4E405 - 2F9B BCCC 8527 692A 04E3  331E FAF8 226A D5D4 E405




reply via email to

[Prev in Thread] Current Thread [Next in Thread]