将带有 `MonadIO` 类型变量的 class 的实例提升到转换后的 monad
Lift instance of class with a `MonadIO` type-variable to the transformed monad
作为我需要从单子计算中记录数据的程序的一部分,我正在尝试定义一个 class 以使其更方便。
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 ()
我想做的一件事是在 'higher' monad 中定义 get
和 put
,因为某些数据在 [=16 中无法访问=].对于更简单的数据,例如 Storable
的实例,IO
就足够了。我想将基本实例保留在 'lowest' 可能的单子中,但允许将操作提升到任何 'higher' 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
其中 IO
中的实例可以与任何 MonadIO
中的实例组合。但是,使用我当前的代码,GHC 无法推断出 Serial m Int32
的实例。对于提升 IO
的特殊情况,这个问题可以通过 liftIO
解决,但如果基类型是 t IO
则不再有效。我认为这可以通过重叠实例来解决,但我想尽可能避免这种情况。有什么办法可以实现吗?
您可以只写出所需的额外约束:
func :: (Serial m a, Serial m Int32) => Handle -> a -> m ()
func h a = put h (0::Int32) >> put h a
(我认为这需要-XFlexibleContexts
。)
如果这使签名变得笨拙,您可以将约束组合在“约束同义词 class”中:
class (Serial m a, Serial m Int32, Serial m Int64, ...)
=> StoSerial m a
instance (Serial m a, Serial m Int32, Serial m Int64, ...)
=> StoSerial m a
func :: StoSerial m a => Handle -> a -> m ()
func h a = put h (0::Int32) >> put h a
作为我需要从单子计算中记录数据的程序的一部分,我正在尝试定义一个 class 以使其更方便。
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 ()
我想做的一件事是在 'higher' monad 中定义 get
和 put
,因为某些数据在 [=16 中无法访问=].对于更简单的数据,例如 Storable
的实例,IO
就足够了。我想将基本实例保留在 'lowest' 可能的单子中,但允许将操作提升到任何 'higher' 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
其中 IO
中的实例可以与任何 MonadIO
中的实例组合。但是,使用我当前的代码,GHC 无法推断出 Serial m Int32
的实例。对于提升 IO
的特殊情况,这个问题可以通过 liftIO
解决,但如果基类型是 t IO
则不再有效。我认为这可以通过重叠实例来解决,但我想尽可能避免这种情况。有什么办法可以实现吗?
您可以只写出所需的额外约束:
func :: (Serial m a, Serial m Int32) => Handle -> a -> m ()
func h a = put h (0::Int32) >> put h a
(我认为这需要-XFlexibleContexts
。)
如果这使签名变得笨拙,您可以将约束组合在“约束同义词 class”中:
class (Serial m a, Serial m Int32, Serial m Int64, ...)
=> StoSerial m a
instance (Serial m a, Serial m Int32, Serial m Int64, ...)
=> StoSerial m a
func :: StoSerial m a => Handle -> a -> m ()
func h a = put h (0::Int32) >> put h a