[英]Lift instance of class with a `MonadIO` type-variable to the transformed monad
作為我需要從 monadic 計算記錄數據的程序的一部分,我試圖定義一個類以使其更方便。
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 ()
我希望能夠做的一件事是定義get
並將其put
“更高”的單子中,因為某些數據在IO
無法訪問。 對於更簡單的數據,例如Storable
的實例, IO
就足夠了。 我想將基本實例保留在“最低”可能的 monad 中,但允許將操作提升到任何“更高”的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 (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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.