Minor tweaks to ffi addendum
While implementing the full FFI for Hugs, I'm coming across a bunch of little things that confuse me and some wee typos. Here's a list of what I've found so far. If no-one objects in the next few days, I'll go ahead and make minor changes to the report where required. section 4.1.1: The definition of impents suggests that there must/can be spaces around the keywords 'dynamic' and 'wrapper' and that the static form can have spaces at start and end. I'm going ahead and implementing this interpretation but want to remark that it seems a bit odd. No action proposed. section 4.1.3: It took me a while to understand the explanation of Dynamic import. After a while, I realized that I should read the type as: (FunPtr ft) -> ft instead of FunPtr (ft -> ft) The current text 'FunPtr ft -> ft' does mean the former but it would be easier to read if written '(FunPtr ft) -> ft'. Proposed change: write the type as '(FunPtr ft) -> ft' Incidentally, how am I to interpret the type rules? Which of the following have the right type: foreign import ccall "dynamic" mkFun1 :: FunPtr (Int -> IO ()) -> (Int -> IO ()) type Int2 = Int foreign import ccall "dynamic" mkFun2 :: FunPtr (Int2 -> IO ()) -> (Int -> IO ()) newtype Int3 = Int3 Int foreign import ccall "dynamic" mkFun3 :: FunPtr (Int3 -> IO ()) -> (Int -> IO ()) data Int4 = Int4 Int foreign import ccall "dynamic" mkFun4 :: FunPtr (Int4 -> IO ()) -> (Int -> IO ()) type T5 = FunPtr (Int -> IO ()) -> (Int -> IO ()) foreign import ccall "dynamic" mkFun5 :: T5 Proposed change: none at present section 5.5: It's going to be really hard to implement ForeignPtr as specified. The problem is that invocation of the cleanup function is triggered by the garbage collector. I don't want the garbage collector to be recursively invoked so I don't want the garbage collector to directly invoke Haskell function. The Hugs garbage collector is not supposed to be a mutator of the heap - so putting a simple wrapper round the garbage colector won't work. And there's no mechanism outside the GC to look for cleanup functions to execute. GHC gets round this by scheduling a (preemptive) thread to execute the cleanup code. How on earth does NHC get round this? Does anyone have a suggestion for how it might be implemented in Hugs? Proposed change: none at present but I'm deeply sceptical of a design which takes a simple task (invoke a C cleanup function) and, for no discernable reason, generalizes it to the point that it requires a much more complex runtime system. section 5.6: castStablePtrToPtr and castPtrToStablePtr can break typesafety so one might want to try to make sure that any program that uses them will be forced to have the word 'unsafe' in them. Proposed change: none at present Table 2 (section 6): Is there a reason to be so coy about the concrete C types used for HsChar, HsFloat, HsDouble and HsBool? Proposed change: C symbol Haskell Symbol Constraint on concrete C type HsChar Char unsigned char HsFloat Float float HsDouble Double double HsBool Bool int I see no reason at all not to specify HsBool. For HsChar, I'd like to know if it is signed or unsigned. I can imagine that the vague specification is to allow for wide characters but it's not clear to me that one could write much portable code without knowing that so it seems we should pick wide or narrow characters and stick with it. (Hmmm, wonder how much code it would take to make Hugs work with wide characters?) For HsFloat/Double, I imagine we're being vague because Hugs implements Double with single precision floats and GHC on the Alpha (used to) implement Float with double precision floats. I think Hugs can be fixed (in fact, was almost all done ages ago). That leaves the question of whether we want to force Haskell implementations to follow C conventions. I'm not sure. Table 3 (section 6): Proposed change: Rename column 1 as 'Preprocessor symbol'. In the last 2 lines, change column 1 to read HS_BOOL_FALSE and HS_BOOL_TRUE. (This change merely standardizes the existing GHC fix for this problem.) -- Alastair Reid reid@cs.utah.edu http://www.cs.utah.edu/~reid/
Alastair Reid wrote:
[...] Incidentally, how am I to interpret the type rules? Which of the following have the right type:
foreign import ccall "dynamic" mkFun1 :: FunPtr (Int -> IO ()) -> (Int -> IO ())
type Int2 = Int foreign import ccall "dynamic" mkFun2 :: FunPtr (Int2 -> IO ()) -> (Int -> IO ())
newtype Int3 = Int3 Int foreign import ccall "dynamic" mkFun3 :: FunPtr (Int3 -> IO ()) -> (Int -> IO ())
data Int4 = Int4 Int foreign import ccall "dynamic" mkFun4 :: FunPtr (Int4 -> IO ()) -> (Int -> IO ())
type T5 = FunPtr (Int -> IO ()) -> (Int -> IO ()) foreign import ccall "dynamic" mkFun5 :: T5
From the top of my head, the following should be correct: * mkFun1: Well, if *that* one isn't... :-) * mkFun2, mkFun5: The FFI should "look through" type synonyms (as usual) * mkFun3: The FFI should "look through" newtypes (for convenience) Wrong: * mkFun4: The FFI does not "look through" data, even in this easy case. (Hmmmm, but the current GHC from CVS allows this...)
[...] castStablePtrToPtr and castPtrToStablePtr can break typesafety so one might want to try to make sure that any program that uses them will be forced to have the word 'unsafe' in them.
The consensus was that every program which uses the FFI is inherently unsafe, so this is not a special problem of the casts. We dropped the 'unsafe' prefix consciously, IIRC.
Table 2 (section 6):
Is there a reason to be so coy about the concrete C types used for HsChar, HsFloat, HsDouble and HsBool?
Yes: The diversity of representations in the current Haskell implementations. I remember dozens of outcries overtime I wanted to be a little bit more specific in the first drafts of this table...
[...] HsChar Char unsigned char
Uh, oh! I'll die with a sudden heart attack... (And Marcin probably too :-} In a nutshell: Haskell Chars are supposed to be Unicode characters, Unicode is even larger than 16bit nowadays, and wchar_t is hopelessly underspecified. I'm not aware of any usable and portable C type for this, uint32_t is coming close, but is a bit misleading.
HsFloat Float float HsDouble Double double
A Haskell implementation could choose to use "double" for "Float" and "long double" for Double, or "float" for everything, or ...
HsBool Bool int
I see no reason at all not to specify HsBool.
Hmmm, I can't remember the reasoning for this vagueness.
[...] Table 3 (section 6):
Proposed change:
Rename column 1 as 'Preprocessor symbol'.
In the last 2 lines, change column 1 to read HS_BOOL_FALSE and HS_BOOL_TRUE. (This change merely standardizes the existing GHC fix for this problem.)
I agree to both change. Cheers, S.
participants (2)
-
Alastair Reid -
Sven Panne