Using higher-order Haskell types in C# -


how can use , call haskell functions higher-order type signatures c# (dllimport), like...

double :: (int -> int) -> int -> int -- higher order function  typeclassfunc :: ... -> maybe int    -- type classes  data mydata = foo | bar              -- user data type datafunc :: ... -> mydata 

what corresponding type signature in c#?

[dllimport ("libhsdlltest")] private static extern ??? foo( ??? ); 

additionally (because may easier): how can use "unknown" haskell types within c#, can @ least pass them around, without c# knowing specific type? important functionality need right know pass around type class (like monad or arrow).

i know how compile haskell library dll , use within c#, first-order functions. i'm aware of stackoverflow - call haskell function in .net, why isn't ghc available .net , hs-dotnet, didn't find documentation , samples (for c# haskell direction).

i'll elaborate here on comment on fuzxxl's post.
examples posted possible using ffi. once export functions using ffi can you've figured out compile program dll.

.net designed intention of being able interface c, c++, com, etc. means once you're able compile functions dll, can call (relatively) easy .net. i've mentioned before in other post you've linked to, keep in mind calling convention specify when exporting functions. standard in .net stdcall, while (most) examples of haskell ffi export using ccall.

so far limitation i've found on can exported ffi polymorphic types, or types not applied. e.g. other kind * (you can't export maybe can export maybe int instance).

i've written tool hs2lib cover , export automatically of functions have in example. has option of generating unsafe c# code makes pretty "plug , play". reason i've choosen unsafe code because it's easier handle pointers with, in turn makes easier marshalling datastructures.

to complete i'll detail how tool handles examples , how plan on handling polymorphic types.

  • higher order functions

when exporting higher order functions, function needs changed. higher-order arguments need become elements of funptr. they're treated explicit function pointers (or delegates in c#), how higher orderedness typically done in imperative languages.
assuming convert int cint type of double transformed

(int -> int) -> int -> int 

into

funptr (cint -> cint) -> cint -> io cint 

these types generated wrapper function (doublea in case) exported instead of double itself. wrapper functions maps between exported values , expected input values original function. io needed because constructing funptr not pure operation.
1 thing remember way construct or dereference funptr statically creating imports instruct ghc create stubs this.

foreign import stdcall "wrapper" mkfunptr  :: (cint -> cint) -> io (funptr (cint -> cint)) foreign import stdcall "dynamic" dynfunptr :: funptr (cint -> cint) -> cint -> cint 

the "wrapper" function allows create funptr , "dynamic" funptr allows 1 deference one.

in c# declare input intptr , use marshaller helper function marshal.getdelegateforfunctionpointer create function pointer can call, or inverse function create intptr function pointer.

also remember calling convention of function being passed argument funptr must match calling convention of function argument being passed to. in other words, passing &foo bar requires foo , bar have same calling convention.

  • user datatypes

exporting user datatype quite straight forward. every datatype needs exported storable instance has created type. instances specifies marshalling information ghc needs in order able export/import type. among other things need define size , alignment of type, along how read/write pointer values of type. partially use hsc2hs task (hence c macros in file).

newtypes or datatypes one constructor easy. these become flat struct since there's 1 possible alternative when constructing/destructing these types. types multiple constructors become union (a struct layout attribute set explicit in c#). need include enum identify construct being used.

in general, datatype single defined as

data single = single  { sint   ::  int                       , schar  ::  char                       } 

creates following storable instance

instance storable single     sizeof    _ = 8     alignment _ = #alignment single_t      poke ptr (single a1 a2) =         a1x <- tonative a1 :: io cint         (#poke single_t, sint) ptr a1x         a2x <- tonative a2 :: io cwchar         (#poke single_t, schar) ptr a2x      peek ptr =          a1' <- (#peek single_t, sint) ptr :: io cint         a2' <- (#peek single_t, schar) ptr :: io cwchar         x1 <- fromnative a1' :: io int         x2 <- fromnative a2' :: io char         return $ single x1 x2 

and c struct

typedef struct single single_t;  struct single {      int sint;      wchar_t schar; } ; 

the function foo :: int -> single exported foo :: cint -> ptr single while datatype multiple constructor

data multi  = demi  {  mints    ::  [int]                     ,  mstring  ::  string                     }             | semi  {  semi :: [single]                     } 

generates following c code:

enum listmulti {cmultidemi, cmultisemi};  typedef struct multi multi_t; typedef struct demi demi_t; typedef struct semi semi_t;  struct multi {     enum listmulti tag;     union multiunion* elt; } ;  struct demi {      int* mints;      int mints_size;      wchar_t* mstring; } ;  struct semi {      single_t** semi;      int semi_size; } ;  union multiunion {     struct demi var_demi;     struct semi var_semi; } ; 

the storable instance relatively straight forward , should follow easier c struct definition.

  • applied types

my dependency tracer emit for type maybe int dependency on both type int , maybe. means, when generating storable instance maybe int head looks

instance storable int => storable (maybe int) 

that is, aslong there's storable instance arguments of application type can exported.

since maybe a defined having polymorphic argument just a, when creating structs, type information lost. structs contain void* argument, have manually convert right type. alternative cumbersome in opinion, create specialized structs aswell. e.g. struct maybeint. amount of specialized structures generated normal module can explode way. (might add flag later on).

to ease loss of information tool export haddock documentation found function comments in generated includes. place original haskell type signature in comment well. ide present these part of intellisense (code compeletion).

as of these examples i've ommited code .net side of things, if you're interested in can view output of hs2lib.

there few other types need special treatment. in particular lists , tuples.

  1. lists need passed size of array marshall from, since we're interfacing unmanaged languages size of arrays not implicitly known. conversly when return list, need return size of list.
  2. tuples special build in types, in order export them, have first map them "normal" datatype, , export those. in tool done untill 8-tuples.

    • polymorphic types

the problem polymorphic types e.g. map :: (a -> b) -> [a] -> [b] size of a , b not know. is, there's no way reserve space arguments , return value since don't know are. plan support allowing specify possible values a , b , create specialized wrapper function these types. on other size, in imperative language use overloading present types you've chosen user.

as classes, haskell's open world assumption problem (e.g. instance can added time). @ time of compilation statically known list of instances available. intend offer option automatically export specialized instances possible using these list. e.g. export (+) exports specialized function known num instances @ compile time (e.g. int, double, etc).

the tool rather trusting. since can't inspect code purity, trust programmer honest. e.g. don't pass function has side-effects function expects pure function. honest , mark higher-ordered argument being impure avoid problems.

i hope helps, , hope wasn't long.

update : there's of big gotcha i've discovered. have remember string type in .net immutable. when marshaller sends out haskell code, cwstring there copy of original. have free this. when gc performed in c# won't affect the cwstring, copy.

the problem when free in haskell code can't use freecwstring. pointer not allocated c (msvcrt.dll)'s alloc. there 3 ways (that know of) solve this.

  • use char* in c# code instead of string when calling haskell function. have pointer free when call returns, or initialize function using fixed.
  • import cotaskmemfree in haskell , free pointer in haskell
  • use stringbuilder instead of string. i'm not entirely sure one, idea since stringbuilder implemented native pointer, marshaller passes pointer haskell code (which can update btw). when gc performed after call returns, stringbuilder should freed.

Comments

Popular posts from this blog

c++ - Is it possible to compile a VST on linux? -

java - Output of Eclipse is rubbish -

jquery - Confused with JSON data and normal data in Django ajax request -