如何将 ad hoc 多态类型声明为特定类型 class 的实例?
How to declare an ad hoc polymorphic type be an instance of an specific type class?
我有一个名为 ManagedValue
的类型 class 定义如下:
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
对于每个作为Storable
类型class实例的类型,它也是ManagedValue
类型class的实例,但不是所有ManagedValue
是可存储的。但是,我不能将它定义为 instance Storable a => ManagedPtr a where ...
之类的东西,因为 GHC 给出错误 The constraint ‘Storable a’ is no smaller than the instance head ‘ManagedValue a’
.
我知道我可以将类型 class 层次结构定义为 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
然后我可以为Int
、Double
等
实现ManagedValue
的方法
感谢任何提示!
您正在寻找 default method signatures:
{-# 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
等,但您不必在这些实例中实现任何内容。
还有另一个选项不需要您为 Storable
的内容手动编写任何 instance
,但它有一些自己的注意事项:
{-# 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
我基本上把类型类从里到外翻转过来,将类型类放在指针类型上,并将值作为关联类型。主要注意事项不再是值类型到指针类型的依赖关系,因此当您实际使用此类型类时,您可能会遇到大量需要使用 TypeApplications
或其他方式解决的歧义类型。第二个警告是 ManagedPtr
现在只能是 Ptr
如果基础值为 Storable
.
我有一个名为 ManagedValue
的类型 class 定义如下:
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
对于每个作为Storable
类型class实例的类型,它也是ManagedValue
类型class的实例,但不是所有ManagedValue
是可存储的。但是,我不能将它定义为 instance Storable a => ManagedPtr a where ...
之类的东西,因为 GHC 给出错误 The constraint ‘Storable a’ is no smaller than the instance head ‘ManagedValue a’
.
我知道我可以将类型 class 层次结构定义为 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
然后我可以为Int
、Double
等
ManagedValue
的方法
感谢任何提示!
您正在寻找 default method signatures:
{-# 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
等,但您不必在这些实例中实现任何内容。
还有另一个选项不需要您为 Storable
的内容手动编写任何 instance
,但它有一些自己的注意事项:
{-# 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
我基本上把类型类从里到外翻转过来,将类型类放在指针类型上,并将值作为关联类型。主要注意事项不再是值类型到指针类型的依赖关系,因此当您实际使用此类型类时,您可能会遇到大量需要使用 TypeApplications
或其他方式解决的歧义类型。第二个警告是 ManagedPtr
现在只能是 Ptr
如果基础值为 Storable
.