Retropikzel's website - Blog - 2024-06-08 - Portable foreign function interface for R7RS small

This is my attemp to get more into blogging and writing. I decided to write about one project I've been working on. Writing this also forces me to write some documentation for the project, which is a good thing to do.

R7RS in this blog post refers to R7RS small.

Note that this mostly describes my journey with Scheme programming, your experiences and life may differ.

Here is the library thats the topic of this posts. https://codeberg.org/r7rs-pffi/pffi

What is a Scheme?

I like to program in Scheme. With any other programmin language saying this propably will not raise any further questions. "Good for you!", people will say and carry on. With Scheme there are some questions that kind of need to be answered:

The answer to the first one, for me, is R7RS. The answer to the second one however is little more complicated. An urge to answer "all of them" is there, and part of me wants to go say "any R7RS conforming Scheme implementation". But not all Scheme implementations, even the ones implementing R7RS as fully as they can, are similar enough that people generally want to use all of them or even multiple implementations. Of course you can pick one implementation that fullfils you needs and go on just programming.

Picking one implementation and using it is fine for most situations. If you just want to get something done, or have fun, or whatever floats your boat then thats the recommended way to go.

Welcome to the family

Sooner or later thought you will use some of the features that are specific to that one implementation you picked. Be it some nice utility function or foreign function interface, usually one that calls C libraries, and then these questions arise:

The first one is more of a philosophical question I think, if you want to program in R7RS only then you propably want to avoid anything thats not in R7RS and if you dont care then you use whatever you need/want. And I will not go any deeper into it here as it would be bikshedding considering this articles topic.

The second question is more practical. You write your library and you use feature only present in implementation A. Lets say you call a foreign function using the implementations ffi procedures.

(a-ffi-call "puts" "Hello there")

And you merrily go on with your day. But then some time later you need/want use implementation B. But your library is kind of locked into implementation A. What to do now?

Luckily R7RS offers a solution! Conditional expanding, or cond-expand between friends. So you write your call again like this.

(cond-expand
    (A (a-ffi-call "puts" "Hello there"))
    (B (b-ffi-call "puts" "Hello there")))

And you merrily go on with your day. Later that night trhought when you lay down to sleep you begin to think:

And that gets us to the present moment.

Same interface on multiple implementations

Now before I continue let me just say that I know that there is no perfect FFI and many things in this library will be some definition of wrong to many people and many situations. The cause for this is obious, I'm not a C developer and I dont have perfect knowledge of every Scheme implementation (or situation) out there.

That said the point of this portable foreign function interface fowever is not to be perfect, or cover 110% of use cases. The point is to put that cond-expand into a nice, easily usable place so

In addition you dont have to know how the Java FFI works as I've written the support for Kawa scheme into the library. And in the future when some of the Scheme implementations running on Javascript gets a (import ...) support then you do not have to know how the NodeJS FFI library works as it should have same interface from scheme as any other after the support is added into this library.

So then you can write the foreign function call as (not real code)

(pffi-call "puts" "Hello there")

and then it will just work on any implementation that the library supports. Releasing you from the burdens of needing to remember or write more.

There will be dragons

As of this blog post the library is in alpha state. The structure is there and I would say it works meaning that it runs on multiple implementations and tests (thought scarce) pass on multiple implementations.

See the git repository for supported implementations and additional info.

Closing words

This post is shorter than I hoped, but I will try to write more in the future. Perhaps going into specifics piece by piece. And of course I have more projects that build on this library that I have to write about.

Notes

While writing this blog post and searching info for it I run into this blog post Cond-expand and #ifdef written by Gwen Weinholt, whose work with Loko scheme I admire a lot. I have read this blog before I started working on this library but it's not (atleas the only) source of inspiration for this library. I had atually forgot that it exists. Anyway I think it's a good read.