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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With