ANN: H98 FFI Addendum 1.0, Release Candidate 7

Ross Paterson ross@soi.city.ac.uk
Sun, 22 Sep 2002 15:34:15 +0100


On Sun, Sep 22, 2002 at 02:49:36PM +1000, Manuel M T Chakravarty wrote:
> Ross Paterson <ross@soi.city.ac.uk> wrote,
> > If the FFI stuff were available with ST variants, and foreign
> > functions [that are pure except for modifying a structure through
> > a pointer] could be declared with ST return types, would it be
> > possible to replace unsafePerformIO in such cases with runST?
> > I'm not suggesting that the FFI spec should do this now, but if
> > there's a clean alternative on the horizon perhaps it would be
> > premature to surrender to unsafePerformIO.
> 
> One problem of using ST is that ST is not H98, but the FFI
> spec is meant to rely on nothing but H98.

No, that's why I wasn't suggesting this for now.

> Another problem is that I don't think you would really gain
> anything.  runST is safe as given the available ST functions
> and the type constraints on runST guarantee that everything
> is fine.  Now with an imported C function you will never
> have any static guarantee beyond your believe of what the C
> routine does.  In particular, you would even break the
> safety guarantees that currently exist for the ST monad, as
> you could now implement "bad" ST functions.  (A similar
> argument can be even made for the marshalling support
> functions, as it is impossible to make static guarantees
> about the safeness of the peek and poke functions.)

Sure, a program using the ffi could break anything if the appropriate
properties don't hold.  All the more reason to be clear about what the
obligations are.

The aim is not to prohibit dishonesty, but rather to enable honesty.
The situation is analogous to foreign functions with pure return types,
which removes many instances of unsafePerformIO.  By writing

	foreign import ccall "math.h sin" c_sin :: CDouble -> CDouble

one asserts that this function is pure.  By writing

	foreign import ccall getparts :: CString -> STPtr s Record -> ST s ()

one would be asserting that this function had no side-effects except for
modifying the record it was given a pointer to.  There would be similar
obligations on people implementing allocators and instances of

	class Storable a where
		...
		peekSTPtr :: STPtr s a -> ST s a
		pokeSTPtr :: STPtr s a -> a -> ST s ()

Put them all together and you have a piece of code that has no effects
outside of the memory region s, and is safe for runST, provided
each primitive conforms to its obligations.  The point is that those
obligations are clearly defined.  Not formally defined, but that's not
what the FFI spec is about.