Guess nobody got my Prolog VM suggestion. Here it is again in its full glory:
It would also be possible to add a rich FFI to SWI-Prolog. Currently SWI-Prolog emits the following Prolog VM code for a poor foreign function stub, where <func> is the function pointer. The code is emitted depending on arity, no type information is used:
Lets assume we have now a foreign function alarm(integer) . What can be generated as rich foreign function stub. In as far the Albufeira Prolog interpreter is not unsuitable, having adopted the approach from the paper is not really an obstacle. New instructions ???1??? and ???2???:
???1??? some guard to check whether the first argument is integer
???2??? some conversion to convert the Prolog tagged pointer
into foreign integer
I_FCALLDET1 alarm
The problem is probably garbage collection etc… since suddently foreign data type arguments are on the stack window. I don’t know how to solve this problem in the case of SWI-Prolog, because I don’t know enough about SWI-Prolog.
So yes, its non trivial somehow. Not sure. Also I_FCALLDETX has hard coded that the arguments are terms with call-by-reference, passing consecutive addresses. A rich FFI has usually also call-by-value, and not only call-by-reference.
While its obvious, i didn’t connect in my mind that it VM instructions that handle foreign interface calls.
From my little understanding i am wondering if it makes sense to have a dedicated VM instruction that dedicated for deterministic foreign calls – and have a designated subset of FFI for such calls – hopefully, simplifying calls, foreign function writing, and call handing a lot – including perhaps a-priori designation of what are input and what are output variables.
And, then have VM instructions that are non-determinstic – the prolog way – that then also puts additional demands on the foreign predicate implementor.
I don’t quite understand why this would have to be a new set of virtual machine instructions. Why not do these checks on the other side, in the compiled function that prolog is calling? That’s where all the context is of what the function is actually going to do with these terms, so that is the place with all the information, where a compiler can determine best how to handle such issues as garbage collection too.
Of course that means it doesn’t just work with a plain c function but it doesn’t have to. You can add an extra compile step in here. This is not uncommon - an example that immediately comes to mind is QT which extends plain c++ with signals and slots, requiring a preprocessing step to properly make that work. And in a language like rust, it can all be done through macros.
Constructing a C call from a typed array of arguments is far from trivial. There are a couple if libraries to that (one used by the SWI-Prolog ffi pack). The ffi pack combines an array of types with an array of term_t types (which you get for free anyway in SWI-Prolog). Besides the portability limitations involved in this approach I stand by the reasons not going into the direction I gave in the post linked below.
I like what I see in the Rust interface!
P.s. The current approach also avoids most of the GC issues. For example, we can pass the native C string associated with an atom as the term_t argument protects the atom from being garbage collected while the foreign code runs.