Home > other >  Convert haskell style type signature to C
Convert haskell style type signature to C

Time:09-16

I have a set of haskell type signature, similar to below: (for anyone that knows haskell, this is a monomorphised version of .. )

(t2 -> t3) -> (t1 -> t2) ->  t1 -> t3

And with the implicit parens (Conveniently, this is how my program currently stores the type signatures - as a tree.):

(t2 -> t3) -> ((t1 -> t2) -> (t1 -> t3))

I am looking for a programmatic way to convert this style of type signature to a C style type signature with function pointers. So far, all I have been able to find is resources concerning one, maybe two levels of function pointers - Obviously, in this case, I need it to support theoretically infinite levels. Any resources or pointers would be helpful.

Thank you in advance.

EDIT: Encoding the type as a C datastructure would also work.

CodePudding user response:

If @Dmitry's comment accurately reflects what you're trying to do, the Haskell code to perform such a conversion is surprisingly simple:

data HType = (:->) HType HType | H String
  deriving (Show)
infixr 0 :->

ctype :: String -> HType -> String
ctype x (a :-> b) = ctype ("(*"    x    ")("    ctype "" a    ")") b
ctype "" (H a) = a
ctype x  (H a) = a    " "    x

main = do
  let t = (H "t2" :-> H "t3") :-> (H "t1" :-> H "t2") :-> H "t1" :-> H "t3"
  putStrLn $ ctype "compose" t

For this example, it produces the type signature:

t3 (*(*(*compose)(t3 (*)(t2)))(t2 (*)(t1)))(t1)

which does, indeed, describe a type compose that's a pointer to a function that accepts a pointer to a function t2 -> t3, returning a pointer to a function that accepts a pointer to a function t1 -> t2 that returns a pointer to a function t1 -> t3.

It's a little hard to see how such a type could be used. I mean, if you want to emit code for a compose function that could actually be assigned to such a pointer, it's tough to do without first-class functions. As a proof of concept, here's a non-reentrant version using global variables that proves that the type "works":

#include <stdio.h>

/* some concrete types to use */
typedef char t1;
typedef int t2;
typedef char* t3;

/* compose :: (t2 -> t3) -> ((t1 -> t2) -> (t1 -> t3)) */
typedef t3 (*(*(*compose)(t3 (*)(t2)))(t2 (*)(t1)))(t1);

/* code defining a `do_compose` function pointer of C type `compose` */

t3 (*f1)(t2);
t2 (*f2)(t1);

t3 compose2(t1 x)
{
        return (*f1)((*f2)(x));
}

t3 (*compose1(t2 (*g)(t1)))(t1)
{
        f2 = g;
        return compose2;
}

t3 (*(*compose0(t3 (*f)(t2)))(t2 (*)(t1)))(t1)
{
        f1 = f;
        return compose1;
}

compose do_compose = compose0;

/*
 * a test case for `do_compose`
 */

/* ord :: t1 -> t2 */
int ord(char c)
{
        return (int)c;
}

/* print :: t2 -> t3 */
char* print(int i)
{
        static char buffer[256];
        sprintf(buffer, "%d", i);
        return buffer;
}

int main()
{
        puts(do_compose(print)(ord)('A'));
}

Alternatively, if you want an "uncurried" version of the type, which in this case would be:

t3 (*compose)(t3 (*)(t2), t2 (*)(t1), t1)

(i.e., compose is a pointer to a function that takes a pointer to a function t2 -> t3, a pointer to a function t1 -> t2, and a value of type t1 and then returns a value of type t3), the Haskell code still isn't too bad:

ctype' :: String -> HType -> String
ctype' "" (H a) = a
ctype' x (H a) = a    " "    x
ctype' x funcall = go [] funcall
  where go args (a :-> b) = go (ctype "" a : args) b
        go args b = ctype ("(*"    x    ")("    intercalate ", " (reverse args)    ")") b

The resulting function is much more ergonomic for implementation in C:

/* compose :: (t2 -> t3) -> ((t1 -> t2) -> (t1 -> t3)), uncurried version */
typedef t3 (*compose)(t3 (*)(t2), t2 (*)(t1), t1);

t3 compose0(t3 (*f)(t2), t2 (*g)(t1), t1 x)
{
        return (*f)((*g)(x));
}

compose do_compose = compose0;

...

int main()
{
        puts(do_compose(print, ord, 'A'));
}
  • Related