HaskellのDLLを作ってRubyから呼ぶ

18. Running GHC on Win32 systems — Glasgow Haskell Compiler 8.6.4 User's Guideを参考にやってみました。Haskellで書いたフィボナッチ関数をDLLにして、それをRubyのWin32APIを使って呼びます。Haskellのコードは必然的にIOになります。
ファイル名: fib.hs

module Fib where

foreign export stdcall fib :: Int -> IO Int

fib :: Int -> IO Int
fib = return . fib'
    where
      fib' 0 = 0
      fib' 1 = 1
      fib' n = fib' (n - 2) + fib' (n - 1)

ファイル名: main.c

#include <Windows.h>
#include "HsFFI.h"

#include "fib_stub.h"

extern void __stginit_Fib(void);

BOOL APIENTRY 
DllMain(HANDLE hModule, 
	DWORD  ul_reason_for_call, 
	LPVOID lpReserved)
{
    return TRUE;
}

HsBool
hs_begin(void)
{
    int argc = 2;
    char *argv[] = {"-B", "C:\\ghc\\ghc-6.8.1", NULL};
    char **argvp = argv;
	
    hs_init(&argc, &argvp);
    hs_add_root(__stginit_Fib);
    
    return HS_BOOL_TRUE;
}

HsBool
hs_end(void)
{
    hs_exit();
    return HS_BOOL_TRUE;
}

DLLの生成例:

$ ghc -c fib.hs -fglasgow-exts
$ ghc -c main.c
$ ghc -shared -o fib.dll fib.o fib_stub.o main.o
Creating library file: fib.dll.a

Rubyからの呼び出しはこんな感じになります。

#!/usr/bin/env ruby

require 'Win32API'

hs_begin = Win32API.new('fib.dll', 'hs_begin', nil,   'i')
hs_end   = Win32API.new('fib.dll', 'hs_end',   nil,   'i')
fib      = Win32API.new('fib.dll', 'fib@4',    ['p'], 'i')

hs_begin.call
begin
  p fib.call(0)  # => 0   
  p fib.call(1)  # => 1   
  p fib.call(2)  # => 1   
  p fib.call(3)  # => 2   
  p fib.call(10) # => 55  
  p fib.call(20) # => 6765
ensure
  hs_end.call
end