call-haskell-from-anything 0.2.0.0 → 1.0.0.0
raw patch · 5 files changed
+27/−113 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- FFI.Anything.TypeUncurry.DataKinds: Proxy :: Proxy k
- FFI.Anything.TypeUncurry.DataKinds: data Proxy k
- FFI.Anything.TypeUncurry.DataKinds: instance FFI.Anything.TypeUncurry.DataKinds.ToTypeList (Data.Functor.Identity.Identity r) '[] r
- FFI.Anything.TypeUncurry.ReturnResult: return1 :: (Monad m) => r -> m r
- FFI.Anything.TypeUncurry.ReturnResult: return2 :: (Monad m) => (a -> r) -> a -> m r
- FFI.Anything.TypeUncurry.ReturnResult: return3 :: (Monad m) => (a -> b -> r) -> a -> b -> m r
- FFI.Anything.TypeUncurry.ReturnResult: return4 :: (Monad m) => (a -> b -> c -> r) -> a -> b -> c -> m r
- FFI.Anything.TypeUncurry.ReturnResult: return5 :: (Monad m) => (a -> b -> c -> d -> r) -> a -> b -> c -> d -> m r
- FFI.Anything.TypeUncurry.ReturnResult: return6 :: (Monad m) => (a -> b -> c -> d -> e -> r) -> a -> b -> c -> d -> e -> m r
- FFI.Anything.TypeUncurry.ReturnResult: return7 :: (Monad m) => (a -> b -> c -> d -> e -> f -> r) -> a -> b -> c -> d -> e -> f -> m r
- FFI.Anything.TypeUncurry.ReturnResult: returnId1 :: r -> Identity r
- FFI.Anything.TypeUncurry.ReturnResult: returnId2 :: (a -> r) -> a -> Identity r
- FFI.Anything.TypeUncurry.ReturnResult: returnId3 :: (a -> b -> r) -> a -> b -> Identity r
- FFI.Anything.TypeUncurry.ReturnResult: returnId4 :: (a -> b -> c -> r) -> a -> b -> c -> Identity r
- FFI.Anything.TypeUncurry.ReturnResult: returnId5 :: (a -> b -> c -> d -> r) -> a -> b -> c -> d -> Identity r
- FFI.Anything.TypeUncurry.ReturnResult: returnId6 :: (a -> b -> c -> d -> e -> r) -> a -> b -> c -> d -> e -> Identity r
- FFI.Anything.TypeUncurry.ReturnResult: returnId7 :: (a -> b -> c -> d -> e -> f -> r) -> a -> b -> c -> d -> e -> f -> Identity r
+ FFI.Anything.TypeUncurry.DataKinds: instance (FFI.Anything.TypeUncurry.DataKinds.Param f ~ '[], FFI.Anything.TypeUncurry.DataKinds.Result f ~ r, f ~ r) => FFI.Anything.TypeUncurry.DataKinds.ToTypeList f '[] r
Files
- call-haskell-from-anything.cabal +1/−2
- src/FFI/Anything/TypeUncurry/DataKinds.hs +21/−45
- src/FFI/Anything/TypeUncurry/Msgpack.hs +3/−5
- src/FFI/Anything/TypeUncurry/ReturnResult.hs +0/−48
- test/Test1.hs +2/−13
call-haskell-from-anything.cabal view
@@ -1,5 +1,5 @@ name: call-haskell-from-anything-version: 0.2.0.0+version: 1.0.0.0 license: MIT author: Niklas Hambüchen (mail@nh2.me) maintainer: Niklas Hambüchen (mail@nh2.me)@@ -34,7 +34,6 @@ exposed-modules: FFI.Anything.TH FFI.Anything.TypeUncurry- FFI.Anything.TypeUncurry.ReturnResult FFI.Anything.TypeUncurry.Msgpack FFI.Anything.TypeUncurry.DataKinds build-depends:
src/FFI/Anything/TypeUncurry/DataKinds.hs view
@@ -12,28 +12,11 @@ -- 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'+-- and then use 'translate' 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+import Data.Proxy -- * Type-level lists (containing types)@@ -72,18 +55,15 @@ -} -- | 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+type family Param f :: [*] where+ Param (a -> f) = a ': Param f+ Param r = '[] -- | 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+type family Result f :: * where+ Result (IO r) = IO r+ Result (a -> f) = Result f+ Result r = r -- | Function f can be translated to 'TypeList' l with result type r.@@ -94,25 +74,28 @@ -- 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@)+-- | Base case: A "pure" function without arguments -- 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@)+-- | Base case: An IO function without arguments (just @IO r@) -- can be translated to @TypeList Nil -> r@. instance ToTypeList (IO r) '[] (IO r) where translate ior Nil = ior +-- | Base case: A value @r@ can be translated to @TypeList Nil -> r@.+instance (Param f ~ '[], Result f ~ r, f ~ r) => ToTypeList f '[] r where+ -- Could also be written as+ -- (Param r ~ '[], Result r ~ r) => ToTypeList r '[] r+ -- but I find the other way clearer.+ translate r Nil = r + -- Now an example: ----- someFunction :: Int -> Double -> Identity String+-- someFunction :: Int -> Double -> String -- someFunction _i _d = return "asdf" -- -- exampleAutoTranslate = translate someFunction@@ -122,24 +105,17 @@ -- * 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 :: Proxy l) paramLength :: Proxy l -> Int instance ParamLength '[] where paramLength _ = 0 instance (ParamLength l) => ParamLength (a ': l) where- paramLength _ = succ $ paramLength (undefined :: Proxy l)-+ paramLength _ = succ $ paramLength (Proxy :: Proxy l)
src/FFI/Anything/TypeUncurry/Msgpack.hs view
@@ -27,7 +27,6 @@ , byteStringToCStringFunIO , export , exportIO-, module FFI.Anything.TypeUncurry.ReturnResult ) where import Data.ByteString (ByteString)@@ -35,12 +34,11 @@ import qualified Data.ByteString.Lazy as BSL import Data.Maybe (fromMaybe) import qualified Data.MessagePack as MSG+import Data.Proxy import Data.Vector (Vector) import qualified Data.Vector as V import Foreign.C -import FFI.Anything.TypeUncurry.ReturnResult- import FFI.Anything.TypeUncurry @@ -79,7 +77,7 @@ errorMsg locationStr = "call-haskell-from-anything: " ++ locationStr ++ ": got wrong number of function arguments or non-array" --- | Translates a function of type @a -> b -> ... -> Identity r@ to+-- | Translates a function of type @a -> b -> ... -> r@ to -- a function that: -- -- * takes as a single argument a 'ByteString' containing all arguments serialized in a MessagePack array@@ -139,7 +137,7 @@ return res_cs --- | Exports a "pure" function (usually it has to be wrapped in the Identity monad)+-- | Exports a "pure" 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!
− src/FFI/Anything/TypeUncurry/ReturnResult.hs
@@ -1,48 +0,0 @@-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-
test/Test1.hs view
@@ -6,9 +6,7 @@ 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 @@ -41,17 +39,8 @@ 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+f1_t = uncurryMsgpack f1 foreign export ccall f1_t_export :: CString -> IO CString f1_t_export :: CString -> IO CString@@ -73,7 +62,7 @@ foreign export ccall fib_export :: CString -> IO CString fib_export :: CString -> IO CString-fib_export = export . returnId2 $ fib+fib_export = export fib -- TODO the sole *presence* of this function seems to make the calls in Python slower