[plt-scheme] Re: image manipulation

From: Eli Barzilay (eli at barzilay.org)
Date: Thu Mar 25 17:01:14 EST 2004

On Mar 25, John Kozak wrote:
> Yes, but they're non-trivial to do nicely, because the standard
> representations for common abstractions are quite different in C and
> scheme.  For example, something like this in C:
>   #define TASTE_SWEET	0x0001
>   #define TASTE_SOUR	0x0002
>   #define TASTE_SALT	0x0004
>   #define TASTE_BITTER	0x0008
>   #define TASTE_UMAMI	0x0010
>   int nibble_snack(char *name,int taste);
>   taste("chowmein",TASTE_SWEET|TASTE_SOUR);
> would be nicely mapped to scheme as something like:
>   (taste 'chowmein '(sweet sour))
> You can use heuristics to spot things like C's pseudo-enum pattern
> (the SCM/w FFI did, AFAICR), but that gets complicated, too (often a
> single word is split into multiple regions under and-masks). Also,
> subtleties in the semantics of structs; unions...

Well, you are right, of course.  You might not even want to
contaminate your namespace by hundreds of bindings if you won't use
them...  But this is a good point that my design follows: in my first
attempt, I had an item on my todo list that should have addressed the
above in the ffi C level.  Then I realized that if I prefer Scheme,
then I should always do things in Scheme -- so instead, I provide a
simple interface that is as thin as possible (you'd use just `int' for
functions that consume the above constants), then in Scheme I can go
wild and do all kinds of sophisticated things.  For example, in the
Scheme level I have this definition:

  ;; Call this with a name (symbol) and a list of symbols, where a symbol can be
  ;; followed by a '= and an integer to have a similar effect of C's enum.
  (define (ffi-make-enum-type name symbols)
    (define sym->int '())
    (define int->sym '())
    (define s->c (string->symbol (format "enum:~a->int" name)))
    (let loop ((i 0) (symbols symbols))
      (unless (null? symbols)
        (when (and (not (null? (cdr symbols))) (eq? '= (cadr symbols)))
          (set! i (caddr symbols))
          (set-cdr! symbols (cdddr symbols)))
        (set! sym->int (cons (cons (car symbols) i) sym->int))
        (set! int->sym (cons (cons i (car symbols)) int->sym))
        (loop (add1 i) (cdr symbols))))
    (register-ffi-type name 'int
      (lambda (x)
        (let ((a (assq x sym->int)))
          (if a
            (cdr a)
            (raise-type-error s->c (symbol->string name) x))))
      (lambda (x) (cond ((assq x int->sym) => cdr) (else #f)))))

And you can see that the only things that I use from the C level is
the built in `int' type, and a pair of functions to convert Scheme to
C and back.  (The same thing holds for all kinds of weird function

Of course there are extreme cases, like LAPACK, or the SCMW interface
to the Windows API -- in those cases you'll want to invest some time
in proper abstractions over the raw C level.  But this is where Scheme
shines compared to C...  The only thing is that I'd want a C header
parser stuck there for cases where the definitions change and you
don't want your code to break in that case.  (Well, provided Windows
comes with its header files...  If not then some of the other formats
that are used to describe dlls.)

But there are many cases where you just want to open up a dll, pull
out some "cool" function (eg, I did it with IBM's ViaVoice to do
speech), and use that to impress friends and relatives.

> [...] (I suspect many dynamic languages would prefer to do the above
> scheme-style rather than C-style).

Definitely, which is why I'm so amazed at a lack of an LGPL tool that
allows it...

          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                  http://www.barzilay.org/                 Maze is Life!

Posted on the users mailing list.