[MLton] Callback to function pointer?

Matthew Fluet fluet@cs.cornell.edu
Thu, 14 Jul 2005 20:03:21 -0400 (EDT)


> >  _address "symbol";		==> MLton.Pointer.t
> >  _store *: 'a;		==> (MLton.Pointer.t * 'a) -> unit
> >  _store "symbol": 'a;	==> 'a -> unit
> >  _import *: 'a;		==> MLton.Pointer.t -> 'a
> >  _import "symbol": 'a;	==> 'a
> >  _export "symbol": 'a;	==> 'a -> unit

I object to using 'a because these primitives stand for a _family_ of
constants, not polymorphic constants; I also think it hides the fact that
there are distinct treatments for function and non-function types.  
Remember, polymorphism in ML is parametric, not intensional.

So, I like the convention of "ptrTy" as a ground type which expands 
(perhaps opaquely) to a type equivalent to MLton.Pointer.t; "cbTy" as a 
ground type which expands to a type equivalent to a C base type; "ffiTy" 
as a ground type which expands to a type equivalent to either a C base 
type or a type equivalent to a function from a tuple of C base types to a 
C base type.  (Note there is a slightly more subtle restriction on 
the argument and result types of a function "ffiTy", but I'll elide this 
for now.)

  Syntax                             | Elaborated type
 ------------------------------------|---------------------
 _address "symbol" : ptrTy;          : ptrTy
 _store * : cbTy;                    : ??? * cbTy -> unit
 _store "symbol": cbTy;              : cbTy -> unit
 _import * : ffiTy;                  : ??? -> ffiTy
 _import "symbol": ffiTy;            : ffiTy
 _export "symbol": ffiTy;            : ffiTy -> unit

> One other nice thing about the above is that it is, I believe,
> strictly an extension of our currently documented FFI.

It is not a strict extension, as the current documentation for "_import *"  
requires it's type annotation to expand to "ptrTy -> (cbTy1 * ... * cbTyn)
-> cbTy"; that is, the pointer type must be in the annotation.  We should 
rather have:

 _import * : ptrTy -> ffiTy;         : ptrTy -> ffiTy;

It also points to a deficency of  "_store *"  which does not specify the 
elaborated type of the pointer component (preventing it's use with an 
opaquely annotated "_address").  We should rather have:

 _store * : ptrTy * cbTy;            : ptrTy * cbTy -> unit;

There is also the incongruity that the annotation on _store/_export does 
not correspond to the type of the elaborated expression, but rather a 
particular component of the type.

I'll also point out that the MLton.Pointer structure does have an 
advantage over _store*/_import* in that the MLton.Pointer functions allow 
for an offset from the base pointer, giving efficient access to C arrays.


I'm happy to see _store added, but I feel that with _store it makes more
sense to make _fetch for C-base types and restrict _import to function
types.  Following that design, I would continue to leave _export
restricted to function types.  I admit that leaves _import * as somewhat
of an outlier; it is a relatively new feature and I could imagine giving
it a distinguished primitive.  Finally, I recognize that this prevents
declaring and allocating space for an object of C base type from ML.  I'm
interested in a real motivating example where this behavior is really
necessary.  (While Wesley's sneaky hacks are interesting, easing the means
by which the type system may be subverted should not be the motivation for
a new feature.)  The only reason to export in the first place is to
interface with C, and if you already have C code lying around why not
declare the objects there.  It is true that you _cannot_ declare/allocate
space for an exported ML function, so _export is strictly necessary there.  
It is also no more efficient to declare an object in ML than in C, whereas
_store/_fetch are strictly more efficient than writing getter/setter
functions in C and _import-ing those.

So, my more radical proposal would be the following, adopting the
convention of "cfTy" as a ground type which expands to a type equivalent
to a function from a tuple of C base types to a C base type.

  Syntax                             | Elaborated type
 ------------------------------------|---------------------
 _address "symbol" : ptrTy;          : ptrTy
 _fetch * : ptrTy -> cbTy;           : ptrTy -> cbTy
 _fetch "symbol" : cbTy;             : cbTy
 _store * : ptrTy * cbTy;            : ptrTy * cbTy -> unit
 _store "symbol": cbTy;              : cbTy -> unit
 _iccall : ptrTy -> cfTy;            : ptrTy -> cfTy
 _import "symbol" : cfTy             : cfTy
 _export "symbol" : cfTy             : cfTy -> unit

And if declaring a C object from ML is truly necessary

 _declare "symbol" : cbTy;           : unit

or possibly

 _declare "symbol" : cbTy -> ptrTy;  : ptrTy

where the dynamic component is to return the address of the symbol 
declared.