Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling Haskell FFI Function Ptrs from C

Tags:

c

haskell

ffi

I am trying to get the following code to work:

sample_hs :: CInt -> (CInt -> CInt)
sample_hs x = (x+)

foreign export ccall sample_hs :: CInt -> (CInt -> CInt)

I would like to be able to do something like this in c:

pf = sample_hs(2);
result = pf(3); //Should be 5;

When I try to do this, however, I get an error message:

error: too few arguments to function ‘sample_hs’

I am guessing that the interface between the language isn't working how I thought it would. Is there a way to do what I'm trying to do?

like image 393
Charles Durham Avatar asked Oct 26 '11 21:10

Charles Durham


2 Answers

It is possible, FFI Does allow Higher-Order functions to be exported. Some modifications to your Haskell is required though:

{-# LANGUAGE ForeignFunctionInterface #-}
module Main where

import Foreign.C.Types
import Foreign

foreign export ccall sample_hs :: CInt -> IO (FunPtr Sample)

type Sample = CInt -> CInt
foreign import ccall "wrapper" mkSample :: Sample -> IO (FunPtr Sample)

sample_hs :: CInt -> IO (FunPtr Sample)
sample_hs x = mkSample (x+) 

main = return ()

Higher-Order functions are exported in Haskell by using the explicit FunPtr type. Just to make it a bit clear I've named the higher ordered type Sample in this case. In order to be able to create a function pointer you need to use a "wrapper" function, hence the extra FFI declaration.

I haven't tested this, But it should work fine, It compiles anyway. More on FunPtr here

-- EDIT I have tested It and it works fine. returns 5 as expected.

If you're by any chance doing this on windows, I have a package on hackage Hs2Lib that would export Haskell functions and compile them to a .DLL automatically for you. It also provides you includes for C/C++ and C#. If You're on Linux however, I'm still working on that.

shameless plug :P

Using Hs2Lib the only thing you need in your file is:

module Test where

-- @@ Export
sample_hs :: Int -> IO (Int -> Int)
sample_hs x = return (x+) 

and a simple call to Hs2lib

PS C:\Users\Phyx\Desktop> hs2lib .\Test.hs
Linking main.exe ...
Done.

The reason for the IO and explicit return is that Int -> (Int -> Int) is just Int -> Int -> Int, since types are right associative. But Int -> IO (Int -> Int) indicates that you want to return a function. It's in IO because creating a function pointer is a side-effecting operation. For completeness the C file used is:

#include <stdio.h>
#include <stdlib.h>
#include "Hs2lib_FFI.h"

/*
 * 
 */
int main(int argc, char** argv) {

    HsStart();

    CBF1_t pf = sample_hs(2);
    int result = pf(3);
    printf("%d\n", result);

    HsEnd();
    return (EXIT_SUCCESS);
}

So It's pretty plug-n-play. But again, It only works for Windows for now.

like image 198
Phyx Avatar answered Nov 06 '22 21:11

Phyx


Although I can't find a clause that specifies it in the FFI, I'm pretty sure that no functions are exported with partial application abilities. The C declaration corresponding to

foreign export ccall sample_hs :: CInt -> CInt -> CInt

is

int sample_hs(int, int);

not

type int (*function_pointer)(int); // or whatever the right syntax is
function_pointer sample_hs(int);

Moreover, the syntax for foreign types forbids exporting higher-order functions -- so function pointers never appear in the declarations on the C side.

like image 21
Daniel Wagner Avatar answered Nov 06 '22 22:11

Daniel Wagner