[Haskell-cafe] RE: Troubles with FFI

Brian Hulley brianh at metamilk.com
Tue May 16 09:56:48 EDT 2006

SevenThunders wrote:
> Done!  Thanks for the tip.
> I added a wiki page on this with my overly simple examples.
> Perhaps I'll extend it as I learn more.
> http://www.haskell.org/hawiki/FfiWithArrays

[From section under matrix1.hs example]

> Now as a Haskell newbie I've been informed that the input array
> that is the second argument of sumarr, should be wrapped in
> an IO monad to keep all access sequential.

No - everything is fine as you've got it at the moment. All access is 
already sequential because the result of your C function is an IO action 
rather than a pure value ie IO CDouble instead of CDouble

> Doing so, however, breaks this code so that it won't compile.
> It seems to me that the withStorableArray access function will only
> return a pointer to the elements that is already wrapped in a monad.

afaiu withStorableArray just returns an IO action, which, when executed, 
will supply the function with the pointer it needs. The result of the IO 
action (the value contained inside the monadic value of type "IO CDouble") 
is the result of the function (in your case a CDouble)

> Shouldn't this already guarantee unique (sequential) access to the 
> pointer?
Yes, because IO actions can only be *executed* in sequence even though they 
can be *created* in any old place (assuming you don't use forkIO etc but 
that's a different story altogether)

> However, what is disturbing about this is what happens if we need
> to replicate the pointer argument over multiple arguments in C
> (or some other language).

This should be fine as long as the Ptr passed to the C function is not 
allowed to escape from the nesting given by withStorableArray - it is only 
valid while the IO action returned by withStorableArray is executing, 
because this is the only point at which the garbage collector is not allowed 
to move the array about.

> Also the business of having to pass
> multiple mutable arrays into one C function call should be
> addressed. For now that remains a TBD in this tutorial.

void multarr(double *mat1Raw, double *mat2Raw, double *resultRaw){
// multiply mat1 by mat2 and store in result matrix
// You could mutate all of the matrices here but for multiplication 
obviously only
// the result needs to be mutated

foreign import ccall multarr :: Ptr Double -> Ptr Double -> Ptr Double -> IO 

main = do
               mat1 <- newListArray ...
               mat2 <- newListArray ...
               result <- newListArray ...
               withStorableArray mat1 (\mat1Raw ->
                    withStorableArray mat2 (\mat2Raw ->
                          withStorableArray result (multarr mat1Raw 

The nested calls to withStorableArray can be avoided by defining your own 
helper functions eg:

      ::  StorableArray i1 e1
     -> StorableArray i2 e2
     -> StorableArray i3 e3
     -> (Ptr e1 -> Ptr e2 -> Ptr e3 -> IO a)
     -> IO a
withStorableArray3 a1 a2 a3 f =
        withStorableArray a1 (\a1Raw ->
               withStorableArray a2 (\a2Raw ->
                    withStorableArray a3 (f a1Raw a2Raw)))

Regards, Brian.

PS: In GHC it's better to use the command line option (or option pragma in 
the source module) -#include "matrix_c.h" because if you put the header in 
the import declaration in GHC you lose inlining optimizations. 

More information about the Haskell-Cafe mailing list