In the process of writing DL1, I also wrote two extension systems, which eventually converged into one extension system called Octext. The basic premise of Octext is based on some observations:
>difficulties in library use stem from various type incompatibilities between languages
>if only a few fixed types are used, there's no need to write FFI bindings for untyped languages
>statically-typed languages can use it directly, layer their own types based on specifications or expose library access to other programs
>the overhead of library calls is proportional to the number of times libraries are called divided by the work done by those libraries per call
>structured binary data is a universal data format
>adding domain elements tends to be sufficient for most practical tasks
>if the user provides a runtime themselves, their own data structures can be used directly by the extension, saving a pass of data marshaling
More obscurely:
>C libraries give the illusion of control by exposing every element of their domain as its own call, while Haskell libraries will casually include DSLs promoting correct use
>the Haskell approach puts less pressure on the API proper and promotes both internal and external correctness
Octext allows a different mode of language and program extension, providing a single agreed interface between language and extension based on structured binary data alongside extension-provided types that remain opaque to the extension's user. Octext takes an additional step to make this practical, it allows the extension user to provide its own runtime to the extension, allowing the native types of the extension user to supply the interface of the extension.
My intention is to allow the user to supply their runtime as one of several vtables, with method gaps filled in programmatically. This way, the extension user can specify its most appropriate set of methods and the extension can use its most appropriate set of methods, with Octext bridging the gap.
Currently, Octext is going some re-design, which I'm hoping should be its last. Currently, I have two major goals. First, I want to include is a direct memory access (DMA) interface, allowing the user runtime to lend it direct pointers to its data structures. Second, I want to update the system so that it can handle concurrency.
The DMA interface I have in mind is simple, but I really should get comments on it. Concurrency is another point to deal with later, but there's a short-term need to resolve that concurrency can be done with the current system. The runtime's complete vtable currently looks like this:
struct octext_runSpec_proto {
octext_dmaWrite_t (*dmaWriteAlloc)(octext_user_t, octext_reqTok_t, size_t);
octext_obj_t (*dmaWriteCons)(octext_user_t, octext_reqTok_t,
octext_dmaWrite_t);
octext_obj_t (*vectorAlloc)(octext_user_t, octext_reqTok_t, size_t);
octext_obj_t (*opaqueCons)(octext_user_t, octext_reqTok_t, octext_tok_t,
octext_opq_t);
bool (*isString)(octext_user_t, octext_reqTok_t, octext_obj_t);
bool (*isVector)(octext_user_t, octext_reqTok_t, octext_obj_t);
bool (*isOpaque)(octext_user_t, octext_reqTok_t, octext_tok_t, octext_obj_t);
size_t (*stringLength)(octext_user_t, octext_reqTok_t, octext_obj_t);
void (*stringRead)(octext_user_t, octext_reqTok_t, size_t, uint8_t *,
octext_obj_t, size_t);
octext_obj_t (*stringUpdate)(octext_user_t, octext_reqTok_t, octext_obj_t,
size_t, size_t, const uint8_t *);
octext_dmaRead_t (*dmaRead)(octext_user_t, octext_reqTok_t, octext_obj_t);
void (*dmaReadFree)(octext_user_t, octext_reqTok_t, octext_dmaRead_t);
octext_dmaWrite_t (*dmaWrite)(octext_user_t, octext_reqTok_t, octext_obj_t);
size_t (*vectorLength)(octext_user_t, octext_reqTok_t, octext_obj_t);
void (*vectorRead)(octext_user_t, octext_reqTok_t, size_t, octext_obj_t *,
octext_obj_t, size_t);
octext_obj_t (*vectorUpdate)(octext_user_t, octext_reqTok_t, octext_obj_t,
size_t, size_t, const octext_obj_t *);
octext_opq_t (*unwrap)(octext_user_t, octext_reqTok_t, octext_tok_t,[Expand Post]
octext_obj_t);
octext_obj_t (*copy)(octext_user_t, octext_reqTok_t, octext_obj_t);
void (*free)(octext_user_t, octext_reqTok_t, octext_obj_t);
};
Right now, I have the slightly ugly notion of giving "request tokens" to every request for concurrency, and I have to think about if it will work and if there is a better way to do it. I also have to settle exactly how DMA should work from a reference-counting perspective.