作为我需要从一元计算记录数据的程序的一部分,我正在尝试定义一个类以使其更方便。
module Serial where
import Data.Int
import Data.IORef
import System.IO
import Control.Monad.Trans
import Foreign.Ptr
import Foreign.Marshal
import Foreign.Storable
class MonadIO m => Serial m a where
get :: Handle -> m a
put :: Handle -> a -> m ()
我想做的一件事是在“更高”的 monad 中定义get
和,因为某些数据在. 对于更简单的数据,例如 的实例,就足够了。我想将基本实例保留在“最低”可能的单子中,但允许将操作提升到任何“更高”实例。put
IO
Storable
IO
MonadIO
instance (Serial m a, MonadIO (t m), MonadTrans t)
=> Serial (t m) a where
get = lift . get
put h = lift . put h
instance Storable a => Serial IO a where
get h = alloca (\ptr
-> hGetBuf h ptr (sizeOf (undefined :: a))
>> peek ptr)
put h a = with a (\ptr
-> hPutBuf h ptr $ sizeOf a)
这个想法是启用功能,如
func :: Serial m a => Handle -> a -> m ()
func h a = put h (0::Int32) >> put h a
其中 in 中的实例IO
可以与 any 中的实例组合MonadIO
。但是,使用我当前的代码,GHC 无法推断Serial m Int32
. 对于解除IO
此问题的特定情况,可以通过 解决liftIO
,但如果基本类型t IO
不再起作用。我认为这可以通过重叠实例来解决,但如果可能的话,我想避免这种情况。有什么办法可以做到这一点?