4
votes

I'm using the FFI in order to use a function in C that takes a struct and returns the same struct. The references I saw say I have to use pointers to these structures in order to be able to import it into Haskell. So, for example.

data Bar = Bar { a :: Int, b :: Int }
type BarPtr = Ptr (Bar)

foreign import ccall "static foo.h foo"
    f_foo :: BarPtr -> BarPtr

Now I have the problem that I have to be able to use the function. The references I saw had functions of type BarPtr -> IO () and used with, which has signature Storable a => a -> (Ptr a -> IO b) -> IO b, which was ok, because they where calling the function inside main.

However, I would like to wrap this function in a library, getting a function of type Bar -> Bar without IO, is it possible to do without unsafePerformIO? What's the procedure?

1
If you want a pure function from something like Ptr A -> IO (), then the correspond C function must be "almost pure" in the sense that the only effect it has is modifying the memory pointed to by is argument. If that is the case, you write a function of type A -> A by using Storable and alloca to create a pointer for C, then reading from that pointer and returning the value. This function is morally pure since it has no observable effect, so calling unsafePerformIO on it is perfectly fine (in fact this is the intended use of unsafePerformIO)user2407038

1 Answers

6
votes

It's not possible to remove IO from the type without using unsafePerformIO. However, it is possible to get a function with the type you want in this case, with some caveats. Specifically the C function "foo" cannot depend upon any global variables, thread-local state, or anything besides the single argument. Also, calling foo(bar) should always provide the same result when bar is unchanged.

I expect that trying to import the C function

bar foo(bar input);

with this call

f_foo :: BarPtr -> BarPtr

will result in a compiler error due to the result type. I think you may need to write a wrapper function (in C):

void wrap_foo(bar *barPtr) {
    bar outp = foo(*barPtr);
    *barPtr = outp;
}

and import it as

f_wrap_foo :: BarPtr -> IO ()

Finally, you would call this imported function with:

fooBar :: Bar -> Bar
fooBar bar = unsafePerformIO $ with bar $ \barPtr -> do
    f_wrap_foo barPtr
    peek barPtr