请注意,副作用的产生不是问题。重要的是“不纯”与“纯”功能。虽然getVal
不会产生副作用,但它依赖于副作用来产生一个值,因为它会咨询presentLocation
. 换句话说,它是一个不纯的函数。
Haskell 可以调用外部函数,无论它们是纯的还是不纯的,你只需要给它们适当的签名。必须给不纯函数一个IO a
返回类型。可以给纯函数一个非IO
返回类型。(当然,你也可以给一个纯函数一个IO
返回类型,但你不必这样做,所以通常不会。)
例如,假设我们有一个简单的 C++“接口”:
int value = 0; // Haskell code sets value directly
extern "C" int getValue() { return value; } // and gets it with this
如果我们错误地尝试getValue
作为纯函数导入:
foreign import ccall "interface.cc &value" valuePtr :: Ptr CInt
foreign import ccall "interface.cc getValue" getValue :: CInt -- **NO NO NO!!!**
并像这样测试它:
main :: IO ()
main = do
print getValue
poke valuePtr 5
print getValue
我们得到不正确的输出:
0
0
相反,我们需要给出getValue
一个类型IO CInt
:
foreign import ccall "interface.cc getValue" getValue :: IO CInt
对程序的其余部分进行适当的修改:
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
输出如预期:
0
5
请注意,只有返回值应该被赋予一个IO
类型。如果我们添加一个带参数的不纯函数,例如:
extern "C" int getMultValue(int scale) { return value*scale; }
那么你会使用:
foreign import ccall "interface.cc getMultValue" getMultValue :: CInt -> IO CInt
完整的程序:
// 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
请注意,当所讨论的函数或变量实际上是方法/实例变量时,事情会变得更加复杂。Haskell 不直接支持使用 C++ 对象,因此您需要构建某种extern "C"
接口并将对象指针作为显式参数传递。如果您在设计过程中遇到麻烦,可能会发布其他问题,我们会尽力提供帮助。