ANN: H98 FFI Addendum 1.0, Release Candidate 7
Dear Haskellers, RC 7 of the FFI Addendum is now available from http://www.cse.unsw.edu.au/~chak/haskell/ffi/ The change log since RC 4, which was the last version circulated via haskell@haskell.org, is appended below. Please review this specification carefully. If no suggestion that leads to a serious change is made within a month, I will freeze this addendum. Thanks, Manuel -=- Changes since RC6: * fixed typos Changes since RC5: * Author list: changed Alastair Reid's institution * 1.4: Clarified the wording * 4.1: Explicitly stated that access to pre-processor symbols is not provided by the FFI * 4.1.1: Removed [lib] from impent syntax and discussion * 4.1.3: Added parentheses round FunPtr ft to make it easier to understand a tolerably complex type. * 4.1.4: Removed all mention of library objects; clarified that header files do not impact the semantics of foreign calls, but may be required for correct code generation by some systems * 5.2: Clarified that all operations in Bits are member functions of the type class. Reverse the meaning of the sign of the second argument for `rotate' and `shift' (this makes it the same as GHC used all the time). `bitSize' on `Integer' etc is now undefined. * 5.5: Finalisers must be external functions to facilitate the implementation on Haskell systems that do not support pre-emptive concurrency. Added mallocForeignPtr and mallocForeignPtrBytes. * 6: Specified that HsBool==int in table2 Relabelled column 1 in table 3 (C symbol -> CPP symbol) Replaced 0 and 1 with HS_BOOL_FALSE/TRUE * 6.1: Clarified that nullPtr (nullFunPtr) coincides with (HsPtr) NULL and (HsFunPtr) NULL, respectively. Allowing multiple calls to hs_init() and clarified the constraints on the relative timing between hs_set_argv() and getProgName/getArgs. Added hs_perform_gc(). Changes since RC4: * 5.6: Clarified documentation of `StablePtr's (RC 5)
On Thu, Sep 19, 2002 at 12:03:34AM +1000, Manuel M T Chakravarty wrote:
RC 7 of the FFI Addendum is now available from
Must it include unsafePerformIO?
Ross Paterson <ross@soi.city.ac.uk> wrote,
On Thu, Sep 19, 2002 at 12:03:34AM +1000, Manuel M T Chakravarty wrote:
RC 7 of the FFI Addendum is now available from
Must it include unsafePerformIO?
Sometimes you have a C function that is pure (and hence, you want to give it a pure type), but as C cannot return complex structures as a function result, you need to allocate a piece of memory, pass a pointer to the C function, and read the result out of that memory area on return of the C function. All this marshaling business requires IO actions; however, it doesn't change the pureness of the overall function. So, you typically want to unsafePerformIO the marshaling business. Cheers, Manuel
On Thu, Sep 19, 2002 at 10:28:36PM +1000, Manuel M T Chakravarty wrote:
Ross Paterson <ross@soi.city.ac.uk> wrote,
On Thu, Sep 19, 2002 at 12:03:34AM +1000, Manuel M T Chakravarty wrote:
RC 7 of the FFI Addendum is now available from
Must it include unsafePerformIO?
Sometimes you have a C function that is pure (and hence, you want to give it a pure type), but as C cannot return complex structures as a function result, you need to allocate a piece of memory, pass a pointer to the C function, and read the result out of that memory area on return of the C function. All this marshaling business requires IO actions; however, it doesn't change the pureness of the overall function. So, you typically want to unsafePerformIO the marshaling business.
If the FFI stuff were available with ST variants, and foreign functions of this sort 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. It also bothers me that though the spec says that the argument to unsafePerformIO must be free of side-effects and independent of the environment, there's no definition of which combinations of the other functions satisfy this requirement. (ST-based types would do this, though.)
Ross Paterson <ross@soi.city.ac.uk> wrote,
On Thu, Sep 19, 2002 at 10:28:36PM +1000, Manuel M T Chakravarty wrote:
Ross Paterson <ross@soi.city.ac.uk> wrote,
On Thu, Sep 19, 2002 at 12:03:34AM +1000, Manuel M T Chakravarty wrote:
RC 7 of the FFI Addendum is now available from
Must it include unsafePerformIO?
Sometimes you have a C function that is pure (and hence, you want to give it a pure type), but as C cannot return complex structures as a function result, you need to allocate a piece of memory, pass a pointer to the C function, and read the result out of that memory area on return of the C function. All this marshaling business requires IO actions; however, it doesn't change the pureness of the overall function. So, you typically want to unsafePerformIO the marshaling business.
If the FFI stuff were available with ST variants, and foreign functions of this sort 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. 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 no 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.)
It also bothers me that though the spec says that the argument to unsafePerformIO must be free of side-effects and independent of the environment, there's no definition of which combinations of the other functions satisfy this requirement. (ST-based types would do this, though.)
I don't really understand your point here. As soon as you import C functions, you can't do anything besides handwaving wrt to the safety of the involved code. (Well, you could try to run the C code through a verifier.) Cheers, Manuel
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.
Ross Paterson <ross@soi.city.ac.uk> wrote,
On Sun, Sep 22, 2002 at 02:49:36PM +1000, Manuel M T Chakravarty wrote:
Ross Paterson <ross@soi.city.ac.uk> wrote, 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.
I agree that it makes sense to specify the obligations to FFI primitives. However, for functions like `peekByteOff' it is rather difficult to even informally state these obligations, as it is, for example, unclear how to define the "boundaries" of raw memory objects; this becomes only worse when these objects weren't allocated by Haskell FFI functions. Even defining that a C functions need to be "pure" is not that strong a statement. sin() may have side effects in building tables to cache same intermediate results in the hope to speed up later computations. All we can ask for is that sin() is referentially transparent. Similarly, we can demand that any expression "unsafePerformIO e" must produce the same result when evaluated multiple times. Nevertheless, any additions to the FFI spec that more precisely identify what use of the FFI primitives is acceptable would surely be a good thing. Manuel
On Thu, 19 Sep 2002 14:23:02 +0100 Ross Paterson <ross@soi.city.ac.uk> wrote:
If the FFI stuff were available with ST variants, and foreign functions of this sort could be declared with ST return types, would it be possible to replace unsafePerformIO in such cases with runST?
Even runST would be unsafe: if haskell performs (and a good lazy language implementation should) the optimization which I think is called "factorizing common subexpressions", ** when you have multiple invocations of the "same" expressions, where "same" means two constants with the same name, or two functions with the same name and the "same" arguments, this expression is evaluated only once, and then, on every occurrence, the first value obtained is used **, then runST is unsafe. Consider this example: print :: String -> ST () myPureFunction = (runST (print "This is to be printed twice"), runST (print "This is to be printed twice") How many times is the string printed? Only once, because (runST (...)) is a pure expression and so it can be factorized. Guess what would happen with multiple lock/unlock of a resource called with runST... This, and many other semantic drawbacks, have lead to the use of monads. So, to be safe, every C call, wich potentially has side effects, *must* be used only in the IO monad, where it's correctly sequenced. But one could say "hey! I got a pure C function, like "int f(int x){return x+1;}", it hurts to be obliged to use it in a monad, only because your haskell doesn't trust my C!". To satisfy theese silly people :) unsafePerformIO has been provided. Hope this helps and sorry for bad english. Vincenzo -- Fedeli alla linea, anche quando non c'è Quando l'imperatore è malato, quando muore,o è dubbioso, o è perplesso. Fedeli alla linea la linea non c'è. [CCCP]
participants (3)
-
Manuel M T Chakravarty -
Nick Name -
Ross Paterson