Haskell支持指针和堆内存的分配和使用,主要使用Foreign模块里面的功能。要使用Haskell的指针类型(主要是Ptr a
)需要满足(Storable a
)。在GHCI中查看class Storable
接口的定义:
Prelude> import Foreign
Prelude Foreign> :i Storable
class Storable a where
sizeOf :: a -> Int
alignment :: a -> Int
peekElemOff :: Ptr a -> Int -> IO a
pokeElemOff :: Ptr a -> Int -> a -> IO ()
peekByteOff :: Ptr b -> Int -> IO a
pokeByteOff :: Ptr b -> Int -> a -> IO ()
peek :: Ptr a -> IO a
poke :: Ptr a -> a -> IO ()
{-# MINIMAL sizeOf,
alignment,
(peek | peekElemOff | peekByteOff),
(poke | pokeElemOff | pokeByteOff) #-}
-- Defined in ‘Foreign.Storable’
我们假设要实现一个代数数据类型的Storable Instance
,参考下面的例子:
1 import Foreign
2
3 data MyData = MyData Int Double deriving(Eq, Show, Read)
4
5 instance Storable MyData where
6 sizeOf _ = 16
7 alignment _ = 8
8 peek ptr = let ptr_x = castPtr ptr :: Ptr Int
9 ptr_y = castPtr (ptr_x `advancePtr` 1) :: Ptr Double
10 in do x <- peek ptr_x
11 y <- peek ptr_y
12 return $ MyData x y
13 poke ptr d = let ptr_x = castPtr ptr :: Ptr Int
14 ptr_y = castPtr (ptr_x `advancePtr` 1) :: Ptr Double
15 MyData x y = d
16 in do poke ptr_x x
17 poke ptr_y y
对于一个带参类型,实现Storable Instance
还需要注意下面的问题:
- 自定义ADT中,要求所有被存储在堆内存的成员数据类型也是
Storable Instance
- 自定义ADT中,
sizeOf
和alignment
两个接口尽量使用_
来作为输入参数,而不要对ADT
数据进行任意部分的求值,否则可能引起malloc
接口的错误,因为malloc
接口会使用undefined
作为构造ADT的参数,一旦求值即会报错 - 要实现上述要求必须开启
ScopedTypeVariable
语法扩展,即作用域类型变量,也就是作为参数类型a
可以用于undefined :: a
例如:
{-# LANGUAGE ScopedTypeVariable #-}
data Vector a = Vector a a a deriving(Eq, Show, Read)
instance Storable a => Storable (Vector a) where
sizeOf _ = sizeOf (undefined :: a) * 3 -- Scoped type variable 'a'
alignment _ = sizeOf (undefined :: a) -- Scoped type variable 'a'
{- ... -}
网友评论