*sigh* I never thought that the "last words" on this topic would be easy,
but on the other hand I never imagined that ISO/ANSI C, POSIX, Unicode,
ISO-10646, Ada95, OCaml, persistent functions, etc. will pop up... It's
surely worth having a look at things other people have done, but here are
some instant thoughts before I delve into The Net.

Regarding 1. Mapping Haskell types to C types:
===========================================================================

1) To be honest, I don't know enough about char/wchar_t/Unicode/ISO-10646
   to make suggestions here. Could somebody more knowledgeable make a
   proposal how these should be handled in the FFI? Keep in mind that this
   affects the reverse mapping, too, and that using good old C Strings
   should be easy.

2) It should be *guaranteed* that HsStablePtr and HsAddr are mapped to
   void* for the reason mentioned by Manuel. Nevertheless, these types are
   not useless, they serve as documentation what the pointer should mean.

3) If ISO C has long long (don't know, my copy of Harbison/Steele doesn't
   mention it), Int64/Word64 should be made mandatory.

4) The include file's name is HsFFI.h, not simply ffi.h.

5) Let's explicitly state that Hs{Int,Word}{8,16,32,64} and ISO C's
   {,u}int{8,16,32,64}_t are the same. Having the HsFoo names around is
   nevertheless handy because it is not clear to me what #include one can
   use in a portable way (e.g. there is no stdint.h on HP-UX 10.20).

6) Add HsBool with a mapping to an arbitrary integral C type, see Fergus'
   point about a Haskell API. (Should we guarantee that False maps to 0 and
   True to something <>0?)

Regarding 2. Mapping C types to Haskell types:
===========================================================================

1) Add the following type synonyms to module FFI (if there is no
   corresponding Haskell type, the implementation should use the "closest"
   one):

   Haskell type |     C type
   -------------+-------------------
   CLLong       | long long
   CULLong      | unsigned long long
   CLDouble     | long double


2) The ANSI types are a little bit more complicated than I thought. So here
   is my first shot:

      * The following table contains type synonyms for integral types:

        Haskell type |   C type
        -------------+-------------
        CPtrdiff     | ptrdiff_t
        CSize        | size_t
        CSigAtomic   | sig_atomic_t
        CWchar       | wchar_t

      * The following table contains type synonyms for numeric types:

        Haskell type |   C type
        -------------+-------------
        CClock       | clock_t
        CTime        | time_t

      * fpos_t may be a complex type, but it is only used as fpos_t*,
        i.e. a pointer to a buffer. So we define CFpos as an abstract type,
        which is an instance of Storable. This way a buffer can be
        allocated (e.g. buf <- mallocElem (undefined::CFpos)), and its
        address passed via the usual Addr type. Although this is not
        extremely elegant, it is a simple way to use fpos_t without adding
        special knowledge about it into foreign import/export.

      * jmp_buf is almost the same story as fpos_t, with the small
        difference that always jmp_buf and not jmp_buf* is
        used. Nevertheless, on every architecture I had a look at (Linux,
        HP-UX, Solaris, OSF) it is a pointer/array. So we can use an
        abstract type CJmpBuf, which is an instance of Storable.

      * div_t/ldiv_t are only used as return values of div/ldiv. For
        completeness one could add something like

           data CDiv  = CDiv  Cint  Cint
           data CLdiv = CLdiv CLong CLong

        and make CDiv/CLdiv instances of Storable. But IMHO it is hardly
        worth the trouble.

      * The application programmer never uses FILE, but always FILE*, so
        there is no need for CFile. Addr can be used instead (modulo the
        typed vs. untyped pointer dispute).

      * I can't see any use for a Haskell type corresponding to va_list. It
        is only used in the macros(!) va_start, va_arg, and va_end.

3) I don't think that we should care about this ancient far/near pointer
   stuff. It is already enough that there are different calling conventions
   on Windows platforms.

4) IMHO it *does* make sense to have a C mapping for

      StablePtr (b -> [Maybe Int] -> IO (Integer, b))

   Why should stable pointers be restricted to basic types? The full range
   of types is very useful, e.g. for bindings of callback-based C APIs,
   which associate arbitrary values with a callback.

   Doing some name mangling from a Haskell type can be a tricky business,
   e.g. one must differentiate between

      StablePtr (a, Maybe b)
      StablePtr (a, Maybe a)
      StablePtr (a, MyOwnModule.Maybe b)
      ...

   This is certainly doable, but is this really worth the trouble? I'm not
   sure and would definitely like to hear some opinions on this topic.

Cheers,
   Sven
-- 
Sven Panne                                        Tel.: +49/89/2178-2235
LMU, Institut fuer Informatik                     FAX : +49/89/2178-2211
LFE Programmier- und Modellierungssprachen              Oettingenstr. 67
mailto:[EMAIL PROTECTED]            D-80538 Muenchen
http://www.informatik.uni-muenchen.de/~Sven.Panne

Reply via email to