packages feed

call-haskell-from-anything (empty) → 0.1.0.2

raw patch · 13 files changed

+914/−0 lines, 13 filesdep +attoparsecdep +basedep +bytestringsetup-changed

Dependencies added: attoparsec, base, bytestring, call-haskell-from-anything, msgpack, mtl, template-haskell

Files

+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ call-haskell-from-anything.cabal view
@@ -0,0 +1,81 @@+name:          call-haskell-from-anything+version:       0.1.0.2+license:       MIT+author:        Niklas Hambüchen (mail@nh2.me)+maintainer:    Niklas Hambüchen (mail@nh2.me)+category:      Network+-- We use Configure to determine the `extra-libraries: HSrts-ghc*.*.*` field.+-- See the ./configure script.+build-type:    Configure+license:       MIT+synopsis:      Python-to-Haskell function calls+stability:     experimental+tested-with:   GHC==7.6.3, GHC==7.8.2+cabal-version: >= 1.10+homepage:      https://github.com/nh2/call-haskell-from-anything+bug-reports:   https://github.com/nh2/call-haskell-from-anything/issues+++extra-source-files:+  configure+  detect-ghc-buildinfo.py+++source-repository head+  type: git+  location: git://github.com/nh2/call-haskell-from-anything.git+++library+  default-language: Haskell2010+  hs-source-dirs:+    src+  exposed-modules:+    FFI.Anything.TH+    FFI.Anything.Copied+    FFI.Anything.TypeUncurry+    FFI.Anything.TypeUncurry.ReturnResult+    FFI.Anything.TypeUncurry.Legacy+    FFI.Anything.TypeUncurry.Msgpack+  if impl(ghc >= 7.6.0)+    exposed-modules:+      FFI.Anything.TypeUncurry.DataKinds+  other-modules:+    FFI.Anything.Util+  build-depends:+      base >= 4 && < 5+    , bytestring >= 0.10.0.0+    , msgpack >= 0.7.1.5 && < 1.0.0+    , template-haskell+    , mtl >= 2.1.2+    , attoparsec >= 0.10.3.0+  ghc-options:+    -Wall -fwarn-unused-imports+++executable call-haskell-from-anything.so+  default-language: Haskell2010+  hs-source-dirs:+    test+  main-is:+    Test1.hs+  build-depends:+      call-haskell-from-anything++    -- Packages that already have version bounds in the library:+    , base+    , bytestring+    , msgpack+    , mtl+  ghc-options:+    -- For building TemplateHaskell with cabal and -dynamic, we have to disable -dynamic sometimes+    -no-hs-main -fPIC -shared -dynamic++  -- If we don't specify the Haskell RTS as extra-libraries, we get errors like+  -- the following when calling from some other language:+  --   /usr/lib/ghc/ghc-prim-0.3.0.0/libHSghc-prim-0.3.0.0-ghc7.6.3.so: undefined symbol: stg_forkOnzh+  --+  -- The `build-type: Configure` with the `./configure` script makes sure that+  -- an `extra-libraries: HSrts-ghc*.*.*` field is added using cabal's .buildinfo infrastructure.+  -- See http://stackoverflow.com/questions/6034526/how-can-configuration-tools-like-sdl-config-be-used-with-a-cabalized-project+  -- and https://github.com/nh2/call-haskell-from-anything/pull/2#issuecomment-68638262.
+ configure view
@@ -0,0 +1,4 @@+#!/usr/bin/env sh++# Pass all arguments that cabal passes us to the python script.+python2 detect-ghc-buildinfo.py $@
+ detect-ghc-buildinfo.py view
@@ -0,0 +1,27 @@+import argparse+import subprocess+++# Get the compiler binary from cabal.+# Enables use of cabal's -w/--with-compiler option.+parser = argparse.ArgumentParser()+parser.add_argument(+  '--with-compiler', default="ghc", type=str,+  help='the compiler passed to cabal configure'+)+args, _unknown = parser.parse_known_args()  # ignore unknown arguments from cabal++ghc_binary = args.with_compiler+++print "Determining GHC version:",+ghc_version = subprocess.check_output([ghc_binary, "--numeric-version"]).strip()+print ghc_version+++with open("call-haskell-from-anything.buildinfo", "w") as f:+  f.writelines('\n'.join([+    "executable: call-haskell-from-anything.so",+    "extra-libraries: HSrts-ghc{0}".format(ghc_version),+    "",  # newline at end of file+  ]))
+ src/FFI/Anything/Copied.hs view
@@ -0,0 +1,61 @@+-- | Contains some functions needed from the msgpack package+-- that are unfortunately not yet exposed.+--+-- See https://github.com/msgpack/msgpack-haskell/pull/34.+module FFI.Anything.Copied where++import           Data.ByteString.Lazy.Builder+import qualified Data.Attoparsec.ByteString as A+import           Data.Bits+import           Data.Monoid+import           Data.Word+import           Text.Printf+++-- Copied from Data.MessagePack.Pack+fromArray :: (a -> Int) -> (a -> Builder) -> a -> Builder+fromArray lf pf arr = do+  case lf arr of+    len | len <= 15 ->+      word8 $ 0x90 .|. fromIntegral len+    len | len < 0x10000 ->+      word8 0xDC <>+      word16BE (fromIntegral len)+    len ->+      word8 0xDD <>+      word32BE (fromIntegral len)+  <> pf arr+++-- Copied from Data.MessagePack.Unpack+parseArray :: (Int -> A.Parser a) -> A.Parser a+parseArray aget = do+  c <- A.anyWord8+  case c of+    _ | c .&. 0xF0 == 0x90 ->+      aget . fromIntegral $ c .&. 0x0F+    0xDC ->+      aget . fromIntegral =<< parseUint16+    0xDD ->+      aget . fromIntegral =<< parseUint32+    _ ->+      fail $ printf "invalid array tag: 0x%02X" c+++parseUint16 :: A.Parser Word16+parseUint16 = do+  b0 <- A.anyWord8+  b1 <- A.anyWord8+  return $ (fromIntegral b0 `shiftL` 8) .|. fromIntegral b1+++parseUint32 :: A.Parser Word32+parseUint32 = do+  b0 <- A.anyWord8+  b1 <- A.anyWord8+  b2 <- A.anyWord8+  b3 <- A.anyWord8+  return $ (fromIntegral b0 `shiftL` 24) .|.+           (fromIntegral b1 `shiftL` 16) .|.+           (fromIntegral b2 `shiftL` 8) .|.+           fromIntegral b3
+ src/FFI/Anything/TH.hs view
@@ -0,0 +1,104 @@+module FFI.Anything.TH where++import Language.Haskell.TH++import Debug.Trace+++++parameters :: Type -> [Type]  -- Result list are "ground" types+parameters t = case t of+  AppT t1 t2 -> parameters t1 ++ parameters t2+  ArrowT     -> []+  ConT name  -> [ConT name]+  -- TODO handle ListT, TupleT and so on+  _          -> error $ "parameters: unhandled Type " ++ show t+++-- TODO better use custom data type, tuples are quite finite+argTypesToTuple :: [Type] -> Type+argTypesToTuple types = foldl f (TupleT n) types+  where+    f a next = AppT a next+    n = length types+++debug :: (Show a, Monad m) => a -> m ()+debug x = trace ("\n" ++ show x ++ "\n") $ return ()+++deriveCallable :: Name -> String -> Q [Dec]+deriveCallable funName exportedName = do+  info <- reify funName+  case info of+    VarI name typ _mDec _fixity -> do+      let _nameString   = nameBase name+          signatureList = parameters typ+          paramTypes    = init signatureList+          returnType    = last signatureList++          typ' = [ SigD+                     (mkName exportedName)+                     (AppT+                       (AppT+                         ArrowT+                         (argTypesToTuple paramTypes)+                       )+                       returnType+                     )+                 ]++      debug typ'+      debug $ pprint typ'+      return []++    _ -> error "deriveCallable: can only derive functions"+++-- Example:+--+--   VarI+--     -- Name+--     FFI.Anything.f+--     -- Type+--     (AppT (AppT ArrowT (ConT GHC.Types.Int)) (AppT (AppT ArrowT (ConT GHC.Types.Double)) (ConT GHC.Base.String)))+--     -- Maybe Dec+--     Nothing+--     -- Fixity+--     (Fixity 9 InfixL)+--+-- Where the type "f :: Int -> Double -> String" is:+--+--   AppT+--     (AppT ArrowT (ConT GHC.Types.Int))+--     (AppT+--       (AppT ArrowT (ConT GHC.Types.Double))+--       (ConT GHC.Base.String)+--     )+--+--+-- The target is: runQ f_hs :: (Int, Double) -> String+-- so e.g.:+--+--   runQ [d| f_hs :: (Int, Double) -> String; f_hs = f_hs |]+--+-- which is:+--+--   [ SigD  -- This is the type+--       f_hs+--       (AppT+--         (AppT+--           ArrowT+--           (AppT+--             (AppT (TupleT 2) (ConT GHC.Types.Int))+--             (ConT GHC.Types.Double)+--           )+--         )+--         (ConT GHC.Base.String)+--       )+--   , ValD  -- This is the unimportant `f_hs = f_hs` part needed for the quasiquoter to complile+--       (VarP f_hs_2)+--       (NormalB (VarE f_hs_2))+--       []+--   ]
+ src/FFI/Anything/TypeUncurry.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE CPP #-}++#if MIN_VERSION_base(4,6,0)+#define USE_DATA_KINDS 1+#endif++#if USE_DATA_KINDS+{-# LANGUAGE DataKinds, TypeOperators #-}+#endif+{-# LANGUAGE TypeOperators, ScopedTypeVariables, FlexibleContexts #-}++-- | For GHC 7.6 or newer, we use (and re-export) "FFI.Anything.TypeUncurry.DataKinds" which uses DataKinds for TypeList to be kind-safe.+--+-- For all other versions, use (and re-export) "FFI.Anything.TypeUncurry.Legacy" which uses a simpler model of TypeList.+module FFI.Anything.TypeUncurry (+#if USE_DATA_KINDS+  -- | You see this because your compiler supports DataKinds.+  module FFI.Anything.TypeUncurry.DataKinds+#else+  -- | You see this because your compiler does not support DataKinds.+  module FFI.Anything.TypeUncurry.Legacy+#endif+) where+++#if USE_DATA_KINDS+import FFI.Anything.TypeUncurry.DataKinds+#else+import FFI.Anything.TypeUncurry.Legacy+#endif
+ src/FFI/Anything/TypeUncurry/DataKinds.hs view
@@ -0,0 +1,145 @@+{-# LANGUAGE DataKinds, GADTs, TypeOperators, MultiParamTypeClasses, FlexibleInstances, TypeFamilies, PolyKinds, ScopedTypeVariables #-}++-- | Converts function arguments to tuple-like types.+--+-- For example, take @f :: a -> b -> c -> r@.+-- This module can convert it to @f' :: (a, b, c) -> r@, at compile time.+--+-- This is especially useful for (de)serialization.+-- Suppose you have a function that takes multiple arguments+-- and you want to obtain all of its arguments from some serialized data.+-- The serialization library will make it very easy to unpack types+-- like tuples/lists, but de-serializing *fuction arguments* is not that simple.+--+-- Using this module, you can write an instance how to unpack the 'TypeList' type,+-- and then use 'translateCall' to make any function take such a single 'TypeList'+-- instead of multiple function arguments.+--+-- There is currently a technical limitation:+-- The result type must be wrapped in the 'Identity' monad.+--+-- Example:+--+-- >-- Assume your library provides some unpack function, e.g. it allows you to write:+-- >unpack someBytestring :: (Int, String, Double)+-- >+-- >-- and you have a function+-- >f :: Int -> String -> Double -> Identity Char+-- >+-- >-- then you can use:+-- >f' :: (Int, String, Double) -> Identity Char+-- >f' = translateCall f+-- >+-- >result = f' (unpack someBytestring)+module FFI.Anything.TypeUncurry.DataKinds where++import           Control.Monad.Identity+++-- * Type-level lists (containing types)++-- NOTE: GHC 7.4 cannot deal with DataKinds+-- (see http://hackage.haskell.org/trac/ghc/ticket/5881)+--+-- This is why we have two separate implementations:+-- - one with DataKinds for GHC >= 7.6+-- - one with a standard Nil / Cons type-level list for older+--   compilers which is not kind-safe++-- | Type-level list that can contain arbitrarily mixed types.+--+-- Example:+--+-- >1 ::: "hello" ::: 2.3 :: TypeList '[Int, String, Double]+data TypeList l where+  Nil :: TypeList '[] -- TODO make singleton list, not empty list, base type?+  (:::) :: a -> TypeList l -> TypeList (a ': l)++-- Right-associativity, like (->)+infixr :::++-- Example: You can write:+--+-- exampleTypeList :: TypeList '[String, Int]+-- exampleTypeList = "a" ::: 3 ::: Nil+++-- * \"Uncurrying\" functions++{- In the following, we try to not use Template Haskell,+   using an instance of (a -> ...) to convert functions to TypeLists automatically+   (similar to how you make variadic functions).+-}++-- | Arguments to a function, e.g. @[String, Int]@ for @String -> Int -> r@.+type family Param f :: [*]+-- | For pure functions, we need an 'Identity' monad wrapper here to not conflict with @a -> f@.+type instance Param (Identity r) = '[]+type instance Param (IO r) = '[]+type instance Param (a -> f) = a ': Param f++-- | The result of a function, e.g. @r@ for @String -> Int -> r@.+type family Result f :: *+-- | For pure functions, we need an 'Identity' monad wrapper here to not conflict with @a -> f@.+type instance Result (Identity r) = r+type instance Result (IO r) = IO r+type instance Result (a -> f) = Result f+++-- | Function f can be translated to 'TypeList' l with result type r.+class (Param f ~ l, Result f ~ r) => ToTypeList f l r where+  -- | Translates a function taking multiple arguments to a function+  -- taking a single 'TypeList' containing the types of all arguments.+  --+  -- Example: @t1 -> ... -> tn -> r@ becomes @TypeList [t1, ..., tn] -> r@.+  translate :: f -> TypeList l -> r++-- | Recursive case: A function of type @a -> ... -> r@+-- can be translated to @TypeList [a, ...] -> r@.+instance ToTypeList (Identity r) '[] r where+  translate (Identity r) Nil = r++-- | Base case: A "pure" function without arguments (just @Identity r@)+-- can be translated to @TypeList Nil -> r@.+instance (ToTypeList f l r) => ToTypeList (a -> f) (a ': l) r where+  translate f (a ::: l) = translate (f a) l++-- | Base case: An IO function without arguments (just @Identity r@)+-- can be translated to @TypeList Nil -> r@.+instance ToTypeList (IO r) '[] (IO r) where+  translate ior Nil = ior+++-- Now an example:+--+-- someFunction :: Int -> Double -> Identity String+-- someFunction _i _d = return "asdf"+--+-- exampleAutoTranslate = translate someFunction+--+-- -- ghci would give as type for this: TypeList ((:) * Int ((:) * Double ([] *))) -> [Char]+++-- * Length of type-level lists++-- | A proxy type that can contain an arbitrary type.+--+-- Needed for some type-level computations, like 'paramLength'.+data Proxy k = Proxy+++-- | Allows to calculate the length of a 'TypeList', at compile time.+--+-- We need to use a 'Proxy' for this.+class ParamLength (l :: [*]) where+  -- | Calculates the length of a type list, put into a proxy. Usage:+  --+  -- >paramLength (undefined :: Proxy l)+  paramLength :: Proxy l -> Int++instance ParamLength '[] where+  paramLength _ = 0++instance (ParamLength l) => ParamLength (a ': l) where+  paramLength _ = succ $ paramLength (undefined :: Proxy l)+
+ src/FFI/Anything/TypeUncurry/Legacy.hs view
@@ -0,0 +1,129 @@+{-# LANGUAGE GADTs, TypeOperators, MultiParamTypeClasses, FlexibleInstances, TypeFamilies, ScopedTypeVariables, FlexibleContexts #-}++-- | Converts function arguments to tuple-like types.+--+-- This module is the LEGACY module not using DataKinds.+-- Compilers supporting DataKinds (e.g. GHC >= 7.6) should use "FFI.Anything.TypeUncurry".+--+-- For example, take @f :: a -> b -> c -> r@.+-- This module can convert it to @f' :: (a, b, c) -> r@, at compile time.+--+-- This is especially useful for (de)serialization.+-- Suppose you have a function that takes multiple arguments+-- and you want to obtain all of its arguments from some serialized data.+-- The serialization library will make it very easy to unpack types+-- like tuples/lists, but de-serializing *fuction arguments* is not that simple.+--+-- Using this module, you can write an instance how to unpack the 'TypeList' type,+-- and then use 'translateCall' to make any function take such a single 'TypeList'+-- instead of multiple function arguments.+--+-- There is currently a technical limitation:+-- The result type must be wrapped in the 'Identity' monad.+--+-- Example:+--+-- >-- Assume your library provides some unpack function, e.g. it allows you to write:+-- >unpack someBytestring :: (Int, String, Double)+-- >+-- >-- and you have a function+-- >f :: Int -> String -> Double -> Identity Char+-- >+-- >-- then you can use:+-- >f' :: (Int, String, Double) -> Identity Char+-- >f' = translateCall f+-- >+-- >result = f' (unpack someBytestring)+module FFI.Anything.TypeUncurry.Legacy where++import           Control.Monad.Identity+++-- * Type-level lists (containing types)++-- | The empty type-level type list+data Nil+-- | A type attached to a type-level type list+data a ::: b++-- | Type-level list that can contain arbitrarily mixed types.+--+-- Example:+--+-- >1 ::: "hello" ::: 2.3 :: TypeList (Int ::: String ::: Double ::: Nil)+data TypeList l where+  Nil :: TypeList Nil+  (:::) :: a -> TypeList l -> TypeList (a ::: l)++-- Right-associativity, like (->)+infixr :::+++-- * \"Uncurrying\" functions++{- In the following, we try to not use Template Haskell,+   using an instance of (a -> ...) to convert functions to TypeLists automatically+   (similar to how you make variadic functions).+-}++-- | Arguments to a function, e.g. @[String, Int]@ for @String -> Int -> r@.+type family Param f+-- | For pure functions, we need an 'Identity' monad wrapper here to not conflict with @a -> f@.+type instance Param (Identity r) = Nil+type instance Param (IO r) = Nil+type instance Param (a -> f) = a ::: Param f++-- | The result of a function, e.g. @r@ for @String -> Int -> r@.+type family Result f+-- | For pure functions, we need an 'Identity' monad wrapper here to not conflict with @a -> f@.+type instance Result (Identity r) = r+type instance Result (IO r) = IO r+type instance Result (a -> f) = Result f+++-- | Function f can be translated to 'TypeList' l with result type r.+class (Param f ~ l, Result f ~ r) => ToTypeList f l r where+  -- | Translates a function taking multiple arguments to a function+  -- taking a single 'TypeList' containing the types of all arguments.+  --+  -- Example: @t1 -> ... -> tn -> r@ becomes @TypeList (t1 ::: ... ::: tn ::: Nil) -> r@.+  translate :: f -> TypeList l -> r++-- | Recursive case: A "pure" function of type @a -> ... -> r@+-- can be translated to @TypeList (a ::: ...) -> r@.+instance (ToTypeList f l r) => ToTypeList (a -> f) (a ::: l) r where+  translate f (a ::: l) = translate (f a) l++-- | Base case: A "pure" function without arguments (just @Identity r@)+-- can be translated to @TypeList Nil -> r@.+instance ToTypeList (Identity r) Nil r where+  translate (Identity r) Nil = r++-- | Base case: An IO function without arguments (just @Identity r@)+-- can be translated to @TypeList Nil -> r@.+instance ToTypeList (IO r) Nil (IO r) where+  translate ior Nil = ior+++-- * Length of type-level lists++-- | A proxy type that can contain an arbitrary type.+--+-- Needed for some type-level computations, like 'paramLength'.+data Proxy k = Proxy+++-- | Allows to calculate the length of a 'TypeList', at compile time.+--+-- We need to use a 'Proxy' for this.+class ParamLength l where+  -- | Calculates the length of a type list, put into a proxy. Usage:+  --+  -- >paramLength (undefined :: Proxy l)+  paramLength :: Proxy l -> Int++instance ParamLength Nil where+  paramLength _ = 0++instance (ParamLength l) => ParamLength (a ::: l) where+  paramLength _ = succ $ paramLength (undefined :: Proxy l)
+ src/FFI/Anything/TypeUncurry/Msgpack.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++#if MIN_VERSION_base(4,6,0)+#define USE_DATA_KINDS 1+#endif++#if USE_DATA_KINDS+{-# LANGUAGE DataKinds, TypeOperators #-}+#endif+{-# LANGUAGE TypeOperators, ScopedTypeVariables, FlexibleContexts #-}++-- | Easy FFI via MessagePack.+--+-- You can use this module to expose any Haskell function to other Programming languages.+--+-- It allows to convert functions that take multiple arguments+-- into functions that take one argument:+-- A 'ByteString' which contains all arguments encoded as a MessagePack array.+--+-- Common use cases:+--+-- * Write functions in fast native Haskell code, compile them into a dynamic.+--   library (@.so@ \/ @.dll@) and call them via C\/Python\/Ruby\/whatever via @dlopen()@ or equivalents.+--+-- * Expose Haskell functions via a socket / the web+module FFI.Anything.TypeUncurry.Msgpack (+  UnpackableRec (..)+, getTypeListFromMsgpackArray+, uncurryMsgpack+, tryUncurryMsgpack+, tryUncurryMsgpackIO+, byteStringToCStringFun+, byteStringToCStringFunIO+, export+, exportIO+, module FFI.Anything.TypeUncurry.ReturnResult+) where++import           Control.Applicative+import qualified Data.Attoparsec.ByteString as A+import           Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.MessagePack as MSG+import           Foreign.C+import           Text.Printf++import FFI.Anything.Copied+import FFI.Anything.Util+import FFI.Anything.TypeUncurry.ReturnResult++-- For GHC 7.6 or newer, this imports a TypeUncurry that uses DataKinds for TypeList to be kind-safe.+-- For older versions, it imports TypeUncurryLegacy which uses a weaker model of TypeList.+import FFI.Anything.TypeUncurry+++-- | Helper to allow writing a 'MSG.Unpackable' instance for 'TypeList's.+--+-- We need this because we have to call 'parseArray' at the top-level+-- 'MSG.Unpackable' instance, but not at each function argument step.+class UnpackableRec l where+  getRec :: A.Parser (TypeList l)++-- | When no more types need to be unpacked, we are done.+#if USE_DATA_KINDS+instance UnpackableRec '[] where -- For GHC 7.6 or newer, use DataKinds.+#else+instance UnpackableRec Nil where+#endif+  getRec = return Nil++-- | Unpack one type by just parsing the next element.+#if USE_DATA_KINDS+instance (MSG.Unpackable a, UnpackableRec l) => UnpackableRec (a ': l) where  -- For GHC 7.6 or newer, use DataKinds.+#else+instance (MSG.Unpackable a, UnpackableRec l) => UnpackableRec (a ::: l) where+#endif+  getRec = (:::) <$> MSG.get <*> getRec++++-- | Parses a tuple of arbitrary size ('TypeList's) from a MessagePack array.+getTypeListFromMsgpackArray :: forall l . (UnpackableRec l, ParamLength l) => A.Parser (TypeList l)+getTypeListFromMsgpackArray = parseArray f+  where+    len = paramLength (Proxy :: Proxy l)+    f n | n == len  = getRec+        -- TODO also print function name+        | otherwise = fail $ printf "getTypeListFromMsgpackArray: wrong number of function arguments: expected %d but got %d" len n+++instance (UnpackableRec l, ParamLength l) => MSG.Unpackable (TypeList l) where+  get = getTypeListFromMsgpackArray++++-- | Translates a function of type @a -> b -> ... -> Identity r@ to+-- a function that:+--+-- * takes as a single argument a 'ByteString' containing all arguments serialized in a MessagePack array+--+-- * returns its result serialized in a 'ByteString' via MessagePack 'MSG.pack'+--+-- This function throws an 'error' if the de-serialization of the arguments fails!+-- It is recommended to use 'tryUncurryMsgpack' instead.+uncurryMsgpack :: (MSG.Unpackable (TypeList l), ToTypeList f l r, MSG.Packable r) => f -> (ByteString -> ByteString)+uncurryMsgpack f = \bs -> lazyToStrictBS . MSG.pack $ (translate f $ MSG.unpack bs)+++-- | Like 'uncurryMsgpack', but for 'IO' functions.+--+-- This function throws an 'error' if the de-serialization of the arguments fails!+-- It is recommended to use 'tryUncurryMsgpackIO' instead.+uncurryMsgpackIO :: (MSG.Unpackable (TypeList l), ToTypeList f l (IO r), MSG.Packable r) => f -> (ByteString -> IO ByteString)+uncurryMsgpackIO f = \bs -> lazyToStrictBS . MSG.pack <$> (translate f $ MSG.unpack bs)+++-- | Like 'uncurryMsgpack', but makes it clear when the 'ByteString' containing+-- the function arguments does not contain the right number/types of arguments.+tryUncurryMsgpack :: (MSG.Unpackable (TypeList l), ToTypeList f l r, MSG.Packable r) => f -> (ByteString -> Either String ByteString)+tryUncurryMsgpack f = \bs -> case MSG.tryUnpack bs of+  Left e     -> Left e+  Right args -> Right . lazyToStrictBS . MSG.pack $ (translate f $ args)+++-- | Like 'uncurryMsgpack', but makes it clear when the 'ByteString' containing+-- the function arguments does not contain the right number/types of arguments.+tryUncurryMsgpackIO :: (MSG.Unpackable (TypeList l), ToTypeList f l (IO r), MSG.Packable r) => f -> (ByteString -> Either String (IO ByteString))+tryUncurryMsgpackIO f = \bs -> case MSG.tryUnpack bs of+  Left e     -> Left e+  Right args -> Right $ lazyToStrictBS . MSG.pack <$> (translate f $ args)+++-- * Exporting++-- TODO implement via byteStringToCStringFunIO?+-- | Transforms a 'ByteString'-mapping function to 'CString'-mapping function+-- for use in the FFI.+byteStringToCStringFun :: (ByteString -> ByteString) -> CString -> IO CString+byteStringToCStringFun f cs = do+  cs_bs <- BS.packCString cs+  let res_bs = f cs_bs+  res_cs <- BS.useAsCString res_bs return+  return res_cs+++-- | Transforms a 'ByteString'-mapping 'IO' function to 'CString'-mapping function+-- for use in the FFI.+byteStringToCStringFunIO :: (ByteString -> IO ByteString) -> CString -> IO CString+byteStringToCStringFunIO f cs = do+  cs_bs <- BS.packCString cs+  res_bs <- f cs_bs+  res_cs <- BS.useAsCString res_bs return+  return res_cs+++-- | Exports a "pure" function (usually it has to be wrapped in the Identity monad)+-- to an FFI function that takes its arguments as a serialized MessagePack message.+--+-- Calling this function throws an 'error' if the de-serialization of the arguments fails!+-- Use 'tryExport' if you want to handle this case.+export :: (MSG.Unpackable (TypeList l), ToTypeList f l r, MSG.Packable r) => f -> CString -> IO CString+export = byteStringToCStringFun . uncurryMsgpack+++-- | Exports an 'IO' function to an FFI function that takes its arguments as a serialized MessagePack message.+--+-- Calling this function throws an 'error' if the de-serialization of the arguments fails!+-- Use 'tryExportIO' if you want to handle this case.+exportIO :: (MSG.Unpackable (TypeList l), ToTypeList f l (IO r), MSG.Packable r) => f -> CString -> IO CString+exportIO = byteStringToCStringFunIO . uncurryMsgpackIO+++-- TODO make equivalent using tryUncurryMsgpack (tryExport)+-- TODO make equivalent using tryUncurryMsgpackIO (tryExport)
+ src/FFI/Anything/TypeUncurry/ReturnResult.hs view
@@ -0,0 +1,48 @@+module FFI.Anything.TypeUncurry.ReturnResult where++import Control.Monad.Identity+++return1 :: (Monad m) => r -> m r+return1 = return++return2 :: (Monad m) => (a -> r) -> a -> m r+return2 f a = return $ f a++return3 :: (Monad m) => (a -> b -> r) -> a -> b -> m r+return3 f a b = return $ f a b++return4 :: (Monad m) => (a -> b -> c -> r) -> a -> b -> c -> m r+return4 f a b c = return $ f a b c++return5 :: (Monad m) => (a -> b -> c -> d -> r) -> a -> b -> c -> d -> m r+return5 f a b c d = return $ f a b c d++return6 :: (Monad m) => (a -> b -> c -> d -> e -> r) -> a -> b -> c -> d -> e -> m r+return6 f a b c d e = return $ f a b c d e++return7 :: (Monad m) => (a -> b -> c -> d -> e -> f -> r) -> a -> b -> c -> d -> e -> f -> m r+return7 f a b c d e = return . f a b c d e -- We don't want to use f again, haha!+++returnId1 :: r -> Identity r+returnId1 = return1++returnId2 :: (a -> r) -> a -> Identity r+returnId2 = return2++returnId3 :: (a -> b -> r) -> a -> b -> Identity r+returnId3 = return3++returnId4 :: (a -> b -> c -> r) -> a -> b -> c -> Identity r+returnId4 = return4++returnId5 :: (a -> b -> c -> d -> r) -> a -> b -> c -> d -> Identity r+returnId5 = return5++returnId6 :: (a -> b -> c -> d -> e -> r) -> a -> b -> c -> d -> e -> Identity r+returnId6 = return6++returnId7 :: (a -> b -> c -> d -> e -> f -> r) -> a -> b -> c -> d -> e -> f -> Identity r+returnId7 = return7+
+ src/FFI/Anything/Util.hs view
@@ -0,0 +1,13 @@+-- | Contains some helper functions.+module FFI.Anything.Util (+  lazyToStrictBS+) where++import           Data.ByteString (ByteString)+import qualified Data.ByteString.Lazy as BSL+import           Data.Monoid+++-- | Could use Data.ByteString.Lazy.toStrict starting from bytestring 0.10.0.0+lazyToStrictBS :: BSL.ByteString -> ByteString+lazyToStrictBS = mconcat . BSL.toChunks
+ test/Test1.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE TemplateHaskell #-}++module Test1 where++import           Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL+import           Data.Monoid+import qualified Data.MessagePack as MSG+import           Control.Monad.Identity++import Foreign.C++-- import FFI.Anything.TH (deriveCallable)+import FFI.Anything.TypeUncurry.Msgpack++++-- | Example function to be called from Python.+f1 :: Int -> Double -> String+f1 i f = "Called with params: " ++ show i ++ ", " ++ show f+++-- To be translated to:+f1' :: ByteString -> ByteString+f1' bs = mconcat . BSL.toChunks $ MSG.pack (uncurry f1 $ msg)+  where+    msg = case MSG.tryUnpack bs of+      Left e  -> error $ "tryUnpack: " ++ e+      Right r -> r+++-- TODO check who deallocs - it seems to work magically!+foreign export ccall f1_hs :: CString -> IO CString+f1_hs :: CString -> IO CString+f1_hs cs = do+    cs_bs <- BS.packCString cs+    let res_bs = f1' cs_bs+    res_cs <- BS.useAsCString res_bs return+    return res_cs+++f1_identity :: Int -> Double -> Identity String+f1_identity a b = return $ f1 a b+++-- Only works in GHC 7.6+-- f1_t :: CString -> IO CString+-- f1_t :: ByteString -> ByteString+-- f1_t = translateCall f1_identity++f1_t :: ByteString -> ByteString+f1_t = uncurryMsgpack f1_identity++foreign export ccall f1_t_export :: CString -> IO CString+f1_t_export :: CString -> IO CString+f1_t_export = byteStringToCStringFun f1_t++++fib :: Int -> Int+fib 0 = 1+fib 1 = 1+fib n = fib (n-1) + fib (n-2)+++fib_print :: Int -> IO Int+fib_print x = putStrLn ("fib_print: " ++ show f) >> return f+  where+    f = fib x+++foreign export ccall fib_export :: CString -> IO CString+fib_export :: CString -> IO CString+fib_export = export . returnId2 $ fib+++-- TODO the sole *presence* of this function seems to make the calls in Python slower+-- foreign export ccall fib_print_export :: CString -> IO CString+-- fib_print_export :: CString -> IO CString+-- fib_print_export = exportIO fib_print++-- -- TODO the sole *presence* of this function seems to make the calls in Python slower+-- foreign export ccall fib_print_export2 :: CString -> IO CString+-- fib_print_export2 :: CString -> IO CString+-- fib_print_export2 = exportIO fib_print+++-- $(deriveCallable 'f1 "f1_hs")+++foreign export ccall fib_export_ffi :: CInt -> CInt+fib_export_ffi :: CInt -> CInt+fib_export_ffi = fromIntegral . fib . fromIntegral