In C

Quick note on calling Scheme from C, since I've already covered things the other way around. Rather than some void printing "hello from Scheme" nonsense I wanted to return actual values, but this example may still be incomplete when it comes to complex objects.

Minimal viable example

(import (chicken foreign))

#>
int cF(int n, int m, int(*schemeF)(int, int)) {
  return schemeF(n, m);
}
<#

(define c-f (foreign-safe-lambda int "cF" int int (function int (int int))))
(define-external (adder (int n) (int m)) int (+ n m))
(print (c-f 1 2 (location adder)))

Compile with csc and run the output.

Points of interest:

  1. When calling Scheme code inside C, one must use (foreign-safe-lambda) for some extra bookkeeping.
  2. Despite cF taking a function pointer as its argument, it seems like I should specify its parameter type with function rather than c-pointer.
  3. (define-external) is used to wrap Scheme code to run in C. Its syntax is odd at first glance, but self-explanatory.
  4. I named the Scheme callback adder. It failed to compile as scheme-add, suggesting that Lispy names aren't valid.
  5. Because the C code still expects a function pointer, I wrap adder in (location) when calling (c-f). It looks like I can write #$c-f instead as shorthand, but I kept it obvious.

Threaded example

See previous notes on inter-thread signaling.

(import (chicken foreign) (srfi-18))

#>
static int TICKS = 0;
void increment(void(*signal)(int)) {
  TICKS += 1;
  signal(TICKS);
}
<#

(define COND (make-condition-variable))

(define increment (foreign-safe-lambda void "increment" (function void (int))))

(define-external (increment_signal (int n)) void
  (begin (condition-variable-specific-set! COND n)
         (condition-variable-broadcast! COND)))

(define (c-looper)
  (print 'c-loop)
  (increment (location increment_signal))
  (thread-sleep! 1)
  (c-looper))

(define (scheme-looper mutex)
  (mutex-lock! mutex)
  (print (condition-variable-specific COND))
  (mutex-unlock! mutex COND)
  (scheme-looper mutex))

(define c-thread (make-thread (lambda () (c-looper))))
(define scheme-thread (make-thread (lambda () (scheme-looper (make-mutex)))))

(thread-start! c-thread)
(thread-start! scheme-thread)
(thread-join! scheme-thread)

Points of interest:

  1. Static global values like TICKS seem to remain stable and unaffected by garbage collection. There are a lot of ways one could track this, like in the condition variable itself.
  2. POSIX sleep() inside of C doesn't seem to play nice. As with anything else, I ultimately envision a sndio loop driven by poll() events. Hopefully that works fine.
  3. I don't know the C FFI type representation for complex Scheme constructs like thread condition variables. I think I've gone about it the right way by just wrapping them in closures and passing a pointer to that: lambda the ultimate.

References

  1. chicken foreign
  2. foreign type specifiers