簡體   English   中英

將具有 `MonadIO` 類型變量的類實例提升到轉換后的 monad

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM