将类型为`MonadIO`的类的实例提升为转换后的monad [英] Lift instance of class with a `MonadIO` type-variable to the transformed monad

查看:67
本文介绍了将类型为`MonadIO`的类的实例提升为转换后的monad的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为程序的一部分,我需要记录单子计算的数据,我试图定义一个类以使其更加方便.

As part of a program where I need to log data from monadic computations, I am trying to define a class to make this more convenient.

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 就足够了.我想将基本实例保留在最低"的monad中,但允许将操作提升到任何较高"的 MonadIO 实例.

One of the things I'd like to be able to do is to define get and put in a 'higher' monad, since some data is inaccessible in IO. For simpler data, like instances of Storable, for example, IO is enough. I'd like to keep the basic instances in the 'lowest' possible monad, but allow the actions to be lifted to any 'higher' MonadIO instance.

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 ,则不再起作用.我认为可以通过重叠实例来解决此问题,但我想尽可能避免这种情况.有什么办法可以做到这一点?

where an instance in IO can be combined with an instance in any MonadIO. However with my current code, GHC cannot deduce the instance for Serial m Int32. For the particular case of lifting IO this problem can be solved by liftIO, but if the base type is t IO that doesn't work anymore. I think this can be solved by overlapping instances, but I would like to avoid that if possible. Is there any way to achieve this?

推荐答案

您只需写出所需的额外约束:

You can just write out the required extra constraint:

func :: (Serial m a, Serial m Int32) => Handle -> a -> m ()
func h a = put h (0::Int32) >> put h a

(我认为这需要 -XFlexibleContexts .)

如果这使签名变得笨拙,则可以将约束归为一个约束同义词类":

If this makes the signatures unwieldy, you can group together constraints in a "constraint synonym 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

这篇关于将类型为`MonadIO`的类的实例提升为转换后的monad的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆