我有一個名為的型別類,ManagedValue定義如下:
class ManagedValue a where
type ManagedPtr a = (r :: *) | r -> a
withManaged :: a -> (ManagedPtr a -> IO b) -> IO b
getManaged :: ManagedPtr a -> IO a
castManagedToPtr :: ManagedPtr a -> Ptr b
castPtrToManaged :: Ptr b -> ManagedPtr a
對于作為Storabletypeclass實體的每個型別,它也是ManagedValuetypeclass的實體,但并非所有的ManagedValues 都是可存盤的。但是,我不能將其定義為類似instance Storable a => ManagedPtr a where ...因為GHC給出錯誤The constraint ‘Storable a’ is no smaller than the instance head ‘ManagedValue a’。
我知道我可以定義一個型別的類層次結構為Num對Intergral像class (Storable a, ManagedValue a) => StorableValue a where ...。但是這種方式需要手動定義所有的實體,很煩人。
我想要的是宣告所有Storable值也是ManagedValue如此,以便我可以在一個地方定義一個實體并自動獲取Int,Double等等的實作,例如:
-- This definition doesn't work
instance Storable a => ManagedValue a where
type ManagedPtr a = Ptr a
withManaged v f = do
fp <- mallocForeignPtr
withForeignPtr fp $ \p -> do
poke p v
f p
getManaged p = peek p
castManagedToPtr = castPtr
castPtrToManaged = castPtr
然后我可以實作ManagedValuefor Int, Double, et al 的方法。
感謝您提供任何提示!
uj5u.com熱心網友回復:
您正在尋找默認方法簽名:
{-# LANGUAGE DefaultSignatures, TypeFamilyDependencies #-}
import Foreign.ForeignPtr
import Foreign.Ptr
import Foreign.Storable
class ManagedValue a where
type ManagedPtr a = (r :: *) | r -> a
type ManagedPtr a = Ptr a
withManaged :: a -> (ManagedPtr a -> IO b) -> IO b
default withManaged :: (Storable a, ManagedPtr a ~ Ptr a) => a -> (ManagedPtr a -> IO b) -> IO b
withManaged v f = do
fp <- mallocForeignPtr
withForeignPtr fp $ \p -> do
poke p v
f p
getManaged :: ManagedPtr a -> IO a
default getManaged :: (Storable a, ManagedPtr a ~ Ptr a) => ManagedPtr a -> IO a
getManaged p = peek p
castManagedToPtr :: ManagedPtr a -> Ptr b
default castManagedToPtr :: ManagedPtr a ~ Ptr a => ManagedPtr a -> Ptr b
castManagedToPtr = castPtr
castPtrToManaged :: Ptr b -> ManagedPtr a
default castPtrToManaged :: ManagedPtr a ~ Ptr a => Ptr b -> ManagedPtr a
castPtrToManaged = castPtr
您仍然需要撰寫instance ManagedValue Int、instance ManagedValue Double等,但您不必在這些實體中實作任何內容。
還有另一個選項不需要您手動instance為 s撰寫任何s Storable,但它有自己的一些警告:
{-# LANGUAGE TypeFamilies #-}
import Foreign.ForeignPtr
import Foreign.Ptr
import Foreign.Storable
class ManagedPtr r where
type ManagedValue r
withManaged :: ManagedValue r -> (r -> IO b) -> IO b
getManaged :: r -> IO (ManagedValue r)
castManagedToPtr :: r -> Ptr b
castPtrToManaged :: Ptr b -> r
instance Storable a => ManagedPtr (Ptr a) where
type ManagedValue (Ptr a) = a
withManaged v f = do
fp <- mallocForeignPtr
withForeignPtr fp $ \p -> do
poke p v
f p
getManaged p = peek p
castManagedToPtr = castPtr
castPtrToManaged = castPtr
I basically turned the typeclass inside-out here, putting the typeclass on the pointer type and having the value be an associated type. The main caveat there's no longer a dependency from the value type to the pointer type, so when you actually use this typeclass, you'll probably have a ton of ambiguous types that you need to resolve with TypeApplications or something. The second caveat is that ManagedPtr can only ever be a Ptr now if the underlying value is Storable.
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/355727.html
上一篇:如何為雙半組制作容器?
