This is the mail archive of the guile@cygnus.com mailing list for the Guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: (lambda (foo) (some-c-function)) - can that C function get foo ?


> Hmm, and when I embed compiled C code in such a lambda impression and have
> this C code call out to scheme, is it possible to make that scheme code be
> compiled into the same lambda block (so I'll effectively have one single
> lambda block with both scheme and C code in it) ?

The C code itself isn't compiled into the lambda, just a reference to it
is in there. This should be good enough to get the effect of having both
scheme and C code in the same lambda block. There is only ever one instance
of the compiled C code in memory, but there can be many references to it.

If the garbage collection is all done correctly then whatever references
there are in the lambda expression will stay around as long as the lambda
expression itself is referenced so effectively it works as one monolithic
function, it should make no difference what is in it.

Mind you, when you say ``the same lambda block'' I hope that you don't
mean the the C code can have access to the local variables of a scheme
syntactic block -- that is never possible to do directly. You can do it
indirectly by passing closures to the C and letting it call them although
it is usually nicer to just pass the values as args to the C function.

My example probably won't suit you too well because I'm not quite sure
what you are doing but suppose you have a C-function that reads a file
in some alien format that (read) can't handle and it returns some SCM value.
Suppose you have it defined as:

SCM special_reader( SCM filename, SCM default_val ) { /* whatever */ }

And from scheme you say:

(special-reader "testfile" 'found-nothing)

But then you might want to have a fallback file that gets checked only
if nothing could be found in the original file so you use a different
method which is more flexible:

SCM special_reader( SCM filename, SCM second_try )
{
	/* reader stuff */
	if( /* have result */ ) return( /* result */ );
	gh_call0( second_try );
}

For a normal situation you do:

(special-reader "testfile" (lambda () 'found-nothing))

For a fallback you do:

(special-reader "test_1_file"
                (lambda () (special-reader "test_2_file"
                                           (lambda () 'still-nothing ))))

So now you want to scan a whole list of files and stop at the first
one that works:

(define (special-scan LIST DEFAULT)
  (cond ((null? LIST) DEFAULT)
	(else (special-reader (car LIST)
			      (lambda () (special-scan (cdr LIST) DEFAULT))))))

(special-scan '("test.1" "test.2" "test.3" "test.4") 'hopeless)

Hopefully, you can see that your C code never actually accesses a scheme
variable other than its arguments, however, it gets the same effect as
being able to access LIST to find the next file to open. If the files were
stored in a tree or something, you sould use the same C code for that.

Note that at the time (special-scan) is created, the variables LIST and DEFAULT
get bound to the args of (special-scan) inside the (lambda) expression and
they keep these bindings even when called from C -- or from wherever, it makes
no difference. I still maintain that this is equivalent to self-compiling
code but other people seem to disagree with this. My feeling is that to
achieve the same result using assembly language you would have to do some
very tricky coding so that implies trickiness is happening somewhere because
eventually everything is really assembly language.

Not that that bothers me because it works, and that is what matters.

One other thing to note is that the lambda expression above gets re-evaluated
every time (special-scan) is called -- it has to because each new call
provides new bindings. When you don't actually NEED bindings that make
references to local variables then it's better to move the (lambda) out
of the defined code (using a let or another define). For example, using

(let ((gimme-nothing (lambda () 'found-nothing)))
  (do ((i 0 (+ i 1)))
      ((> i 1e6))
    (special-reader "testfile" gimme-nothing)))

is better than 

(do ((i 0 (+ i 1)))
    ((> i 1e6))
  (special-reader "testfile" (lambda () 'found-nothing)))

because only one lambda expression needs to be created.

> > Yeah, good old global variables -- theorists have worked hard to kill
> > them but somehow they always come back, probably because the work so well.
> > Or as the FORTRAN programmers say, ``I sure love my COMMON block!''.
> 
> Well, I think they're good to hold some reasonable defaults for the callbacks.
> Unlike fortran or C it is very easy in scheme to "locally modify" such global
> variables in a dynamic wind thunk.

I've also found that depending on globals to pass info around is faster
than embedding bindings into the lambdas, the difference is not particularly
large though and often the lambda is more elegant and easier to extend
to handling new situations.

	- Tel

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]