Par
monad,在distributed-process中的Process
,在parallel中的Eval
等等。我选取了两个Haskell库的例子,它们的目的是与数据库后端进行IO交互。我选取的例子分别是riak用于Riak IO,以及hedis用于Redis IO。
在hedis中,定义了一个
Redis
monad(单子)。然后,您可以通过以下方式运行与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 monad之上没有额外的monad实例完成的。
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包引入了一个名为
Redis
的单子来封装使用runRedis
的redis IO操作。相比之下,riak包在withConnection
中只是接受一个接受Connection
的函数,并在IO单子中执行它。那么,定义自己的Monad实例和Monad堆栈的动机是什么?为什么riak和redis包在这方面采取了不同的方法?
Redis a
和Connection -> IO a
大致相当。因此,这基本上是一种表面差异,类似于env-> IO a
与ReaderT env IO a
的比较。 - C. A. McCannCodensity IO Connection
才是他一直想要的单子。 - Gabriella Gonzalez