Open file in haskell, passing filepath in C FFI call (CString)

156 views Asked by At

I want to open a file in Haskell, but I want the top level function to be called from C (I want to pass the filepath from C).

I'm having trouble getting the filepath CString into a type that I can use readFile on.

Here's my first attempt, adapting the example from the docs:

{-# LANGUAGE ForeignFunctionInterface #-}

import Foreign.C.Types
import Foreign.C (CString, peekCString)

openFileDoStuff :: String -> IO Bool
openFileDoStuff filename = do
    lines <- (fmap lines . readFile) filename

    print lines
    -- do stuff with lines

    return True

openFilepathHs :: CString -> IO Bool
openFilepathHs cstr = openFileDoStuff (peekCString cstr)

foreign export ccall openFilepathHs :: CString -> IO Bool

I get a compiler error passing (peekCString cstr) to openFileDoStuff:

• Couldn't match type: IO String
                 with: [Char]

If I change the signature of my function to openFileDoStuff :: IO String -> IO Bool, I then can't use the filename parameter in the readFile call:

• Couldn't match type: IO String
                 with: [Char]

If it's not abundantly clear, I am a newbie in Haskell. I know there's no way to convert IO String -> String, but there must be a way to actually use the CString type.

2

There are 2 answers

0
Daniel Wagner On BEST ANSWER

Use >>= to combine IO actions.

openFilepathHs cstr = peekCString cstr >>= openFileDoStuff

Actually, this pattern of passing a piece of data through successive IO transformations is so common it has a standard combinator for abbreviation.

openFilepathHs = peekCString >=> openFileDoStuff

You can also use do syntax to hide calls to >>=, but as a beginner I personally found do syntax very difficult to understand before I understood how to make calls to >>= myself.

openFilepathHs cstr = do
    cstrContents <- peekCString cstr
    openFileDoStuff cstrContents
1
Aidenhjj On

I needed to run the IO String and bind it to a variable:

{-# LANGUAGE ForeignFunctionInterface #-}

import Foreign.C.Types
import Foreign.C (CString, peekCString)

openFileDoStuff :: IO String -> IO Bool
openFileDoStuff filename = do
    filenameString <- filename
    lines          <- (fmap lines . readFile) filenameString

    print lines
    -- do stuff with lines

    return True

openFilepathHs :: CString -> IO Bool
openFilepathHs cstr = openFileDoStuff (peekCString cstr)

foreign export ccall openFilepathHs :: CString -> IO Bool