這是一個與API設計實踐有關的問題,用於爲Haskell庫定義您自己的Monad實例。定義Monad實例似乎是隔離DSL的一個好方法,例如monad-par,hdph中的monad:Par
Process
分佈式進程; Eval
並行等...何時(以及何時不定義)定義Monad
我拿兩個haskell庫的例子,其目的是IO與數據庫後端。我使用的示例爲Riak IO的riak,Redis IO的hedis。
在hedis中,一個Redis
monad is defined。從那裏,你Redis的身份運行IO:
data Redis a -- instance Monad Redis
runRedis :: Connection -> Redis a -> IO a
class Monad m => MonadRedis m
class MonadRedis m => RedisCtx m f | m -> f
set :: RedisCtx m f => ByteString -> ByteString -> m (f Status)
example = do
conn <- connect defaultConnectInfo
runRedis conn $ do
set "hello" "world"
world <- get "hello"
liftIO $ print world
在了Riak,情況就不同了:
create :: Client -> Int -> NominalDiffTime -> Int -> IO Pool
ping :: Connection -> IO()
withConnection :: Pool -> (Connection -> IO a) -> IO a
example = do
conn <- connect defaultClient
ping conn
爲runRedis
文檔說:「runRedis的每次調用需要從連接的網絡連接並且運行給定的Redis動作,因此調用runRedis可能會阻止來自池中的所有連接正在使用。「。然而,riak包也實現連接池。這一方案無需額外的單子情況下完成的IO單子的頂部:
create :: Client-> Int -> NominalDiffTime -> Int -> IO Pool
withConnection :: Pool -> (Connection -> IO a) -> IO a
exampleWithPool = do
pool <- create defaultClient 1 0.5 1
withConnection pool $ \conn -> ping conn
所以,這兩個包之間的類比可以歸結爲這兩個功能:
runRedis :: Connection -> Redis a -> IO a
withConnection :: Pool -> (Connection -> IO a) -> IO a
至於我可以告訴大家, hedis軟件包引入了一個monad Redis
來使用runRedis
用redis封裝IO操作。相反,withConnection
中的riak包只需要一個採用Connection
的函數,並在IO monad中執行該函數。
那麼,定義自己的Monad實例和Monad堆棧的動機是什麼?爲什麼riak和redis包在解決這個問題上存在差異?
作爲答案的上下文 - 如果不明顯,「Redis a」和「Connection - > IO a」類型大致相同。所以這實質上是一種表面上的區別,與'env - > IO a'和''ReaderT env IO a''相比。 –
然後,這意味着也許這兩個都是不正確的,'Codensity IO Connection'是他一直想要的monad。 –