Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to choose right Haskell C type?

Tags:

c

types

haskell

ffi

I have studied some Haskell programming language and now I found out that it's possible to call Haskell functions from C programs. During my Haskell studies, I created a word frequency counter with Haskell and I would like to try to call that function from a C program, but I don't know how to do it. I found these two websites on haskell.org:

Calling Haskell from C

Foreign C types (Haskell module)

Despite that, I'm a bit lost which types to use. My haskell programs is a pipeline of following functions:

putStr . unlines . map testF . sortedTree

where my own functions

  • testF is type of testF :: Show a => ([Char],a) -> [Char]
  • sortedTree is type of sortedTree :: (Num a, Ord a) => [Char] -> [([Char],a)]

I'm quite sure that I need to convert the types of each function to C types, instead of converting just the function that calls the pipeline. The type of the "main" function is

fileFreq :: [Char] -> IO ()

Besides all this, I'm using a Haskell binary tree, that isn't prelude type.

Here is the whole Haskell code:

module WordCounter where

import List
import Char
import Foreign.C.Types

data BTree a = Tip | BNode a (BTree a) (BTree a) deriving Show

insertFreq x Tip = BNode (x,1) Tip Tip
insertFreq x (BNode (q,p) l r)  | (map toLower x)==(map toLower q)  = BNode (q, p+1) l r
                | otherwise                 = BNode (q,p) l (insertFreq x r)

tlist :: BTree a -> [a]
tlist Tip = []
tlist (BNode x l r) = concat [tlist l, [x], tlist r]

sortedTree x = sortBy (\(x,y) (p,q) -> compare q y) (tlist (foldr insertFreq Tip (words x)))

testF (x, n) = concat (x : ":" : " \t\t\t " : show n : [])

concord = putStr . unlines . map testF . sortedTree

fileFreq filename = do { text <- readFile filename; concord text }

Can anyone guide me a bit with this?

like image 797
zaplec Avatar asked Feb 07 '11 18:02

zaplec


1 Answers

What you will need to do is to create wrapper functions for the functions you need exposed to C and there do the work of converting from C-types to haskell types.

You will also need to enable the ForeignFunctionInterface extension, also any exceptions that may occur in the haskell code need to be handled in the wrapper functions.

For example, if you only need to expose your top-level function fileFreq to C you could add a function like:

fileFreq_hs :: CString -> IO CInt
fileFreq_hs cstr = catch (wrap_fileFreq cstr) (\_ -> return (-1))
  where wrap_fileFreq = do
          str <- peekCString cstr
          fileFreq str
          return 0

to create a function that marshals a C-string into a haskell string (using functions from Foreign.C.String), calls your fileFreq function and translates exceptions to C error codes (-1 if exception happened, 0 otherwise).

Then you need to export it using

foreign export ccall fileFreq_hs :: CString -> IO CInt

and of course you need to add:

{-# LANGUAGE ForeignFunctionInterface #-}

at the top of your module.

Then you can follow the instructions in the links you provided to compile this to a C-stub and header file and create a C-file that you can compile with ghc.

It is of course possible to wrap any function you have, you just need to make sure to handle possible exceptions and to marshal between C-types and haskell types.

The complete code with my modifications is:

{-# LANGUAGE ForeignFunctionInterface #-}
module WordCounter where

import List
import Char
import Foreign.C.Types
import Foreign.C.String
import Control.Monad

data BTree a = Tip | BNode a (BTree a) (BTree a) deriving Show

insertFreq x Tip = BNode (x,1) Tip Tip
insertFreq x (BNode (q,p) l r)  | (map toLower x)==(map toLower q)  = BNode (q, p+1) l r
                | otherwise                 = BNode (q,p) l (insertFreq x r)

tlist :: BTree a -> [a]
tlist Tip = []
tlist (BNode x l r) = concat [tlist l, [x], tlist r]

sortedTree :: (Ord t, Num t) => String -> [([Char], t)]
sortedTree x = sortBy (\(x,y) (p,q) -> compare q y) (tlist (foldr insertFreq Tip (words x)))

testF :: (Show t) => ([Char], t) -> [Char]
testF (x, n) = concat (x : ":" : " \t\t\t " : show n : [])

concord = putStr . unlines . map testF . sortedTree

fileFreq filename = do { text <- readFile filename; concord text }

fileFreq_hs :: CString -> IO CInt
fileFreq_hs cstr = catch (wrap_fileFreq cstr) (\_ -> return (-1))
  where wrap_fileFreq cstr = do
          str <- peekCString cstr
          fileFreq str
          return 0
foreign export ccall fileFreq_hs :: CString -> IO CInt
like image 63
dnaq Avatar answered Sep 28 '22 07:09

dnaq