FFI(Foreign Function Interface: 외부 함수 인터페이스)는 이름 그대로 Haskell과 다른 언어에서 서로 함수를 불러 쓸 수 있도록 해주는 인터페이스다. 기본적인 사용법은 매우 간단해서 어떤 함수를 사용할 지 선언하기만 하면 된다. FFI를 사용하기 위한 선언의 형태는 기본적으로 다음과 같다.
foreign [import/export] [호출규약] "[C 함수 이름]" [Haskell 함수 이름] :: 자료형
foreign은 FFI 선언을 나타내는 키워드이다. 외부 함수를 불러들일 때는 import, Haskell 함수를 외부로 내보낼 때는 export를 사용한다. 호출규약은 언어에 따라 다른데 C는 ccall, C++은 cplusplus, Java 가상 머신은 jvm, 닷넷 프레임워크는 dotnet, 윈도 API는 stdcall을 사용한다. 대부분의 경우 ccall만을 사용한다. 그 다음엔 C의 함수 이름과 Haskell의 함수 이름을 써주고 자료형을 명시해주면 된다. C의 함수 이름에 따옴표를 쳐주는 이유는 Haskell과 함수 이름을 짓는 방법이 다르기 때문이다.
foreign import ccall "string.h strfry" c_strfry :: CString -> IO CString
위 예는 C의 string.h에 선언된 char* strfry(char* string) 함수를 c_strfry라는 Haskell 함수로 불러들이는 FFI 선언문이다. CString은 C의 char*에 해당하는 자료형이다. 반환값은 CString으로 하든 IO CString으로 하든 컴파일에는 상관이 없지만 실행할 때마다 의미가 달라지는 함수라면 IO를 붙여주는 게 현명하다. strfry는 문자열을 랜덤하게 섞어주는 함수로 당연히 IO를 붙여줘야 한다. 이번에는 좀 더 긴 예를 보도록 하자. 다음 두 개의 파일을 먼저 만든다.
/* add.c */
int add(int x, int y){
return x + y;
}
-- main.hs import Foreign.C foreign import ccall "add" c_add :: CInt -> CInt -> IO CInt main = c_add 3 5 >>= print
그리고 쉘에서 ghc -ffi main.hs add.c라고 입력하면 add.c를 포함해서 컴파일하여 바이너리로 만들어준다. Foreign.C 모듈은 C 함수를 불러들일 때 기본적으로 필요한 자료형들을 포함한다. CInt는 C의 int에 대응하는 Haskell 자료형이다.