[Haskell-cafe] Troubles with FFI

Matthew Bromberg mattcbro at earthlink.net
Sat May 13 03:39:14 EDT 2006


I am a newcomer to Haskell and I'm offering my perspective on the troubles I
endured trying to make Haskell interface to some simple C programs.
It is my hope, that by documenting this, someone else will benefit from my
mistakes and errors and climb the learning curve somewhat faster than me.

I was attracted to Haskell, after playing around with OCAML,
and realizing that functional programming offers some
nice advantages to scientific programming. Mainly because the notation and
thinking is very mathematical.

But first, in order to evaluate the suitability of this language for my 
purposes, I had to verify that the foreign function interface (FFI) was 
suitable.
There are a number of numerical libraries, all in imperative languages, 
that I
want to link to.

I quickly discovered that the documentation on Haskell's FFI
is rather poor currently.  This topic is simply not of interest to the 
academics I suppose. It took many hours of searching to find this document:
http://www.cs.caltech.edu/courses/cs11/material/haskell/papers/tackling_the_awkward_squad.pdf
This little tutorial was a goldmine of information about monads and an
introduction to the FFI.  (No template solutions for my problem sorry to 
say.)

It was certainly dismaying to see that even the simplest IO requires at 
least a
 minimal understanding of monads, and adds a bunch of extra semantics to 
the
problem.  However none of the solutions offered, even the automated ones,
seemed to handle the case of passing numerical arrays to C, and having them
get updated and passed back.  That is my primary interest in the FFI
and yet no obvious solution or example could be found after days of 
internet
searching and pouring over tutorials etc.

My conclusion, after hours of studying the hierarchical libraries was that
I needed to use the StorableArray type which led me to this site,
http://www.haskell.org/haskellwiki/Arrays#StorableArray_.28module_Data.Array.Storable.29

This site proposes the following curious piece of code:
 {-# OPTIONS_GHC -fglasgow-exts #-}
 import Data.Array.Storable
 import Foreign.Ptr
 import Foreign.C.Types
 
 main = do arr <- newArray (1,10) 37 :: IO (StorableArray Int Int)
           readArray arr 1 >>= print
           withStorableArray arr $ \ptr ->
               memset ptr 0 40
           readArray arr 1 >>= print
 
 foreign import ccall unsafe "string.h"
     memset  :: Ptr a -> CInt -> CSize -> IO ()

After having freshly read the tutorial on monads I thought I was ready for
this, but I ran into some parsing problems. I don't have a full grasp
of the precedence rules for the operators, >>=  ,  $ and function 
evaluation
are, and whether they group left to right or right to left. A little type
parsing on the inputs and outputs allowed me to resolve all but the line
 withStorableArray arr $ \ptr ->
               memset ptr 0 40

Which should resolve to a monad if I understand the do notation correctly.

My first assumption was that $ has lower precedence than function 
application,
but a higher precendence than >> or >>=, so I assumed the grouping,
 (withStorableArray arr) $ (\ptr -> (memset ptr 0 40))



Now withStorableArray has the type specification
StorableArray i e -> (Ptr e -> IO a) -> IO a ,
so it should be obvious how to use it  (Not).  
At any rate it takes
two arguments so I might assume that  (withStorableArray arr) curries 
the first argument so
that this expression is a function of type
(Ptr e -> IO a) -> IO a .  

Because of the type of the array, it appears that
a = Int. However (\ptr -> (memset ptr 0 40)) should be a function of type
Ptr a -> IO(a) in order to qualify as the second argument of 
withStorableArray.
This almost fits, but not quite.  memset should return type
IO(Int) for this to work correctly, however according to it's type 
declaration
it returns IO ().  Shouldn't the compiler complain about this?


So after about 10 hours of wrestling with syntax,  including messing up
the initial capitalization of module names,  indenting in do lists, 
forgetting
to put commas in tuples and lists I finally got the following example to
compile without error or warning

Matrix.hs
__________

import Foreign
import Foreign.C
import Foreign.C.Types (CInt, CDouble )
import Data.Array.Storable


foreign import ccall "matrix_c.h sumarr" sumarr :: Ptr CDouble -> CDouble

main = do
    arr <- newListArray (1 , 3) [3,2,1]:: IO (StorableArray Int CDouble)
    -- extract the pointer to arr
    dsum <- withStorableArray arr (\ptr -> return (sumarr ptr ))
    print dsum
        

matrix_c.c
------------------            
#include <math.h>

double sumarr(double *in)
{
  return(in[0] + in[1]) ;
}
    

This is how I compiled it using ghc 6.4.2

 > ghc -c matrix_c.o matrix_c.c
 > ghc -O   -o Matrix.exe -fglasgow-exts Matrix.hs matrix_c.o

It runs correctly
 > Matrix
 > 5.0

Notes:
----------------------------------------------
Matrix.exe fails to work correctly if the header file
matrix_c.h is not included in the foreign import statement

The hack? where the second argument of withStorableArray
is a -> IO() instead of a -> IO(a) allows the do clause
to be simplified to
main = do
    arr <- newListArray (1 , 3) [3,2,1]:: IO (StorableArray Int CDouble)
    withStorableArray arr $ print . sumarr

If one changes the array in[] in the C code, e.g.
double sumarr(double *in)
{
  in[0] = -10.0 ;
  return(in[0] + in[1]) ;
}
The array is changed inside the haskell program as a side effect.

main = do
    arr <- newListArray (1 , 3) [3,2,1]:: IO (StorableArray Int CDouble)
    -- extract the pointer to arr
    withStorableArray arr $ print . sumarr
    (readArray arr 1 ) >>= print
I suppose this is one way to 'pass' arrays back to Haskell.



More information about the Haskell-Cafe mailing list