[EMAIL PROTECTED] (Marcin 'Qrczak' Kowalczyk) wrote,
[..]
> The drawback of marsh* is that calls are not uniform. Sometimes
> marsh* are not needed, sometimes they are not sufficient (with array
> and its length passed as separate arguments), initial arguments are
> simpler to pass by partial application than by (return x :> forget),
> and in effect various calls look differently, each using a different
> shortcut. I have no presise idea how to make them better, sorry,
> but I believe it's possible. It could be good if they imposed
> little visual overhead for cases they do nothing special, like for
> (return x :> forget). And still be as simple as possible (but no more).
The `marsh*' family is an attempt to come up with an as
uniform as possible way to express a wide range of common
marshalling situations in pure Haskell. It has to cope with
the following problems:
* Some arguments need IO operations for marshalling.
* Most foreign calls are themselves IO operations.
* Some arguments need pre-processing, some prost-processing,
and some both.
* The pre- and post-processing can be quite different for
different types, but there are many common operations
(like allocating a container to pass as a pointer
argument).
For a single argument of fixed type, it is not too
difficult - eg for `String's:
marshString :: (Addr -> IO a) -> String -> IO a
marshString str op =
do
addr <- stringToAddr str
op addr
free addr
which we use as in
newWindow `marshString` "My window"
Unfortunately, this function is of no use if there is more
than one argument that requires marshalling - the IO monad
kills compositionality here.
Nevertheless, we can generalise this a little:
type MarshSpec a ca = (a -> IO ca, ca -> IO ())
marsh :: MarshSpec a ca -> a -> (ca -> IO b) -> IO b
marsh (pre, post) arg op =
do
carg <- pre arg
op carg
post carg
string :: MarshSpec
string = (stringToAddr, free)
which we use as in
newWindow `marsh` string $ "My window"
Now, we can provide a family of such `marsh' functions
(marhs1, marhs2, etc) for handling external functions that
require the marshalling of 1, 2, etc arguments. With a
little more generalisation, we can also handle inout
parameters - the result is basically the code contained in
the C2HS marshalling library.
I am sure it is possible to improve the use of the library
by providing shortcuts for things like (return x :> forget).
I am happy to incorporate them, just let me know what you
think are the most common patterns.
I am not so sure whether it is possible to `radically'
improve the design, however. Nevertheless, I may have
missed something and I would be most happy about good
suggestions. Please remember that this is not only a C->HS
issue. We plan to have a Haskell marshalling library to
simplify coding bindings on the plain FFI and there we have
exactly the same problems - so, the design of the library
matters beyond C->Haskell.
I have set up a page
http://www.cse.unsw.edu.au/~chak/haskell/c2hs/lib/
with the latest library code for anybody interested in
having a look.
Sven, you might want to link to the above page from the "FFI
proposal page" (instead of the, I think, older versions of
the modules that you are linking at the moment).
Cheers,
Manuel