I know how to call pure C++ functions from Haskell, but wondering how to get GHC to accept functions that have side effects.
I want Haskell to have read-only access to a C++ linked list, and exclusive write access to a C++ variable. For example:
class node {
// Some other class makes a linked list out of these
int x;
node* previous;
node* next;
}
class myHaskellInterface {
node* presentLocation; // C++ decides what is regarded as current location
int getVal() {
// Haskell calls this to get information from C++
return presentLocation->x;
}
int haskellResults; // Store Haskell output here
void setVal(int x) {
// Haskell calls this to pass information back
haskellResults = x;
}
};
Even though getVal() doesn't have side effects, it is part of a class that obviously has side effects, so it's not clear to me whether sneaky tricks are required to get GHC to accept it.
setVal(int) clearly has side effects, so how do you make GHC not care?
Note that the production of side effects isn't the issue. It's "impure" versus "pure" functions that are important. While getVal
doesn't cause side effects, it relies on side effects to produce a value because it consults presentLocation
. In other words, it's an impure function.
Haskell can call foreign functions whether they are pure or impure, you just need to give them appropriate signatures. An impure function must be given an IO a
return type. A pure function can be given a non-IO
return type. (Of course, you could give a pure function an IO
return type, too, but you don't have to and so usually wouldn't.)
For example, suppose we have the simple C++ "interface":
int value = 0; // Haskell code sets value directly
extern "C" int getValue() { return value; } // and gets it with this
If we incorrectly try to import getValue
as a pure function:
foreign import ccall "interface.cc &value" valuePtr :: Ptr CInt
foreign import ccall "interface.cc getValue" getValue :: CInt -- **NO NO NO!!!**
and test it like so:
main :: IO ()
main = do
print getValue
poke valuePtr 5
print getValue
we get incorrect output:
0
0
Instead, we need to give getValue
a type IO CInt
:
foreign import ccall "interface.cc getValue" getValue :: IO CInt
With appropriate modifications to the rest of the program:
import Foreign
import Foreign.C
foreign import ccall "interface.cc &value" valuePtr :: Ptr CInt
foreign import ccall "interface.cc getValue" getValue :: IO CInt
main :: IO ()
main = do
print =<< getValue
poke valuePtr 5
print =<< getValue
the output is as expected:
0
5
Note that it's only the return value that should be given an IO
type. If we add an impure function that takes arguments, like:
extern "C" int getMultValue(int scale) { return value*scale; }
then you'd use:
foreign import ccall "interface.cc getMultValue" getMultValue :: CInt -> IO CInt
The full programs:
// interface.cc
int value = 0;
extern "C" int getValue() { return value; }
extern "C" int getMultValue(int scale) { return value*scale; }
-- Main.hs
import Foreign
import Foreign.C
foreign import ccall "interface.cc &value" valuePtr :: Ptr CInt
foreign import ccall "interface.cc getValue" getValue :: IO CInt
foreign import ccall "interface.cc getMultValue" getMultValue :: CInt -> IO CInt
main :: IO ()
main = do
print =<< getValue
poke valuePtr 5
print =<< getValue
print =<< getMultValue 5
Note that things will get a little more complicated when the functions or variables in question are actually methods / instance variables. Haskell doesn't directly support working with C++ objects, so you need to build some kind of extern "C"
interface and pass object pointers as explicit arguments. If you run into trouble a little further along in your design, maybe post additional questions and we'll try to help.