Hs2lib-0.5.7: FFI.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TypeSynonymInstances #-}
import Control.Applicative
import Control.Monad
import Foreign.C
import Foreign.Ptr
import Foreign.Storable
import Foreign.Marshal.Array
class Context t where
type Collapse t :: *
type Cxt t :: * -> *
collapse :: t -> Collapse t
newtype PureCxt a = PureCxt { unwrapPure :: a }
instance Functor PureCxt where
fmap f = PureCxt . f . unwrapPure
instance Applicative PureCxt where
pure = PureCxt
(<*>) = \(PureCxt f) (PureCxt x) -> PureCxt (f x)
instance Monad PureCxt where
return = pure
m >>= k = k $ unwrapPure m
-- strip out pure contexts, only needs to look at one layer
instance Context (PureCxt a) where
type Collapse (PureCxt a) = a
type Cxt (PureCxt a) = PureCxt
collapse = unwrapPure
-- merge IO contexts using join
instance Context (IO (IO a)) where
type Collapse (IO (IO a)) = IO a
type Cxt (IO (IO a)) = IO
collapse = join
-- remove contexts underneath IO... this might need to be recursive. haven't
-- thought through all the ways contexts can stack up yet.
instance Context (IO (PureCxt a)) where
type Collapse (IO (PureCxt a)) = IO a
type Cxt (IO (PureCxt a)) = IO
collapse = fmap unwrapPure
-- defer IO on a function to only the result. definitely recursive here.
instance (Context (IO b)) => Context (IO (a -> b)) where
type Collapse (IO (a -> b)) = a -> Collapse (IO b)
type Cxt (IO (a -> b)) = IO
collapse x y = collapse $ fmap ($ y) x
-- should probably rethink to what extent I want to separate these...
type family ForeignCxt int :: * -> *
type instance ForeignCxt () = PureCxt
class Convert ext int | ext -> int, int -> ext where
type Foreign int :: *
type Native ext :: *
toForeign :: Native ext -> ForeignCxt (Native ext) (Foreign int)
toNative :: Foreign int -> ForeignCxt (Foreign int) (Native ext)
instance Convert () () where
type Foreign () = ()
type Native () = ()
toForeign = pure
toNative = pure
type instance ForeignCxt Int = PureCxt
type instance ForeignCxt CInt = PureCxt
instance Convert CInt Int where
type Foreign Int = CInt
type Native CInt = Int
toForeign = pure . fromIntegral
toNative = pure . fromIntegral
type instance ForeignCxt Double = PureCxt
type instance ForeignCxt CDouble = PureCxt
instance Convert CDouble Double where
type Foreign Double = CDouble
type Native CDouble = Double
toForeign = pure . realToFrac
toNative = pure . realToFrac
type instance ForeignCxt Float = PureCxt
type instance ForeignCxt CFloat = PureCxt
instance Convert CFloat Float where
type Foreign Float = CFloat
type Native CFloat = Float
toForeign = pure . realToFrac
toNative = pure . realToFrac
type instance ForeignCxt String = IO
type instance ForeignCxt CWString = IO
instance Convert CWString String where
type Foreign String = CWString
type Native CWString = String
toForeign = newCWString
toNative = peekCWString
-- a quick and dirty way to represent arrays; the Int is because we need a size
-- to convert from an array, and the newtype is because otherwise instances
-- for [a] would overlap with String (which is actually [Char])
-- it would probably be nice to handle the (Int,_) as a context, actually,
-- but I'm not sure what the most effective way to do that would be.
type SizedArray a = (Int, Ptr a)
newtype AsArray a = AsArray { getSizedArray :: [a] }
-- instance Newtype (AsArray a) [a] where
-- pack = AsArray
-- unpack = getSizedArray
type instance ForeignCxt (AsArray a) = IO
type instance ForeignCxt (SizedArray a) = IO
instance (Storable a) => Convert (SizedArray a) (AsArray a) where
type Foreign (AsArray a) = (SizedArray a)
type Native (SizedArray a) = (AsArray a)
toForeign xs = let xs' = getSizedArray xs in (,) (length xs') <$> newArray xs'
toNative = fmap AsArray . uncurry peekArray
class FFImport ext where
type Import ext :: *
ffImport :: ext -> Import ext
class FFExport int where
type Export int :: *
ffExport :: int -> Export int
class A a where
type Res a :: *
foo :: a -> Res a
instance A Int where
type Res Int = Int
foo x = x + 4
-- instance ( Context (IO (ForeignCxt a (Native a)))
-- , Convert a (Native a)
-- ) => FFImport (IO a) where
-- type Import (IO a) = Collapse (IO (ForeignCxt a (Native a)))
-- ffImport x = collapse $ toNative <$> x
-- instance ( FFImport b, Convert a (Native a)
-- , Context (ForeignCxt (Native a) (Import b))
-- , Functor (ForeignCxt (Native a))
-- ) => FFImport (a -> b) where
-- type Import (a -> b) = Native a -> Collapse (ForeignCxt (Native a) (Import b))
-- ffImport f x = collapse $ ffImport . f <$> toForeign x
-- instance ( Context (IO (ForeignCxt a (Foreign a)))
-- , Convert (Foreign a) a
-- ) => FFExport (IO a) where
-- type Export (IO a) = Collapse (IO (ForeignCxt a (Foreign a)))
-- ffExport x = collapse $ toForeign <$> x
-- instance ( FFExport b, Convert (Foreign a) a
-- , Context (ForeignCxt (Foreign a) (Export b))
-- , Functor (ForeignCxt (Foreign a))
-- ) => FFExport (a -> b) where
-- type Export (a -> b) = Foreign a -> Collapse (ForeignCxt (Foreign a) (Export b))
-- ffExport f x = collapse $ ffExport . f <$> toNative x
-- instance ( FFImport (a -> b)
-- , FFExport (c -> d)
-- , Context (Native a -> Collapse (ForeignCxt (Native a) (Import b)))
-- ) => Convert (a -> b) (c -> d) where
-- type Native (a -> b) = Import (a -> b)
-- type Foreign (c -> d) = Export (c -> d)
-- toNative = collapse . ffImport