FFIs are Hard

Or, why you can't even add two numbers in Passerine yet.

I figure someone will ask this question eventually, so I'm writing this response now. Foreign Functional Interfaces are hard. Not conceptually speaking — I could probably crank (an admittedly bad one) out in an afternoon — but in terms of designing a solution with an active admonition of traits Passerine strives to embody.

Above all else, Passerine strives to be a concise language, in terms of design and implementation. What does this mean? I mean concision on two fronts:

These goals are slightly conflicting. To build a system where everything composes together perfectly requires Good Architecture™ to be in place. Essentially, MxN problems need to be reduced to Mx1, 1xN problems wherever possible.

So right, back to Passerine. Why can't you add you numbers yet?

Adding, like any other operation, is a function. There's nothing too special about adding when compared to subtraction, multiplication, etc. – two operands in, one value out.

What's my point? To simplify Passerine on the two fronts mentioned above, I'm opting to build an extensible FFI system into Passerine, through which I'll implement performance critical parts of the standard library as well as common operations on data. Instead of defining an add operator in the language itself (which dispatches on type to perform the correct operation) Passerine will simply use FFI bindings to do this.

It's critical that this FFI be fast, which is why it'll bind to Rust, and be compiled against the language core. In the future, I hope that something like this is possible:

// /ffi/thing.rs
use passerine::{self, Data, Runtime};

fn some_ffi_fn(data: Data) -> Result<Data, Runtime> {
    // ...
-- /src/main.pn
use extern thing.some_ffi_fn

print some_ffi_fn ("Hello, World", 27.5)

This is great because it allows for two things:

  1. New operations can be quickly added to passerine. It'll be literally updating the lexer and parser, then writing a few lines of Rust
  2. This FFI will allows users to define their own Rust functions that can be called from Passerine allowing for easier integration with existing Rust libraries.

Of course, before this is possible, a few things need to be done:

Of course, there are a few quality-of-life things I need to organize before I get started with this:

I appreciate the interest. As always, if you have any questions, comments, or suggestions, you know where to find me!

Have a nice one y'all,
Isaac Clayton