{-# 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