Note that there are some explanatory texts on larger screens.

plurals
  1. POWhen (and when not) to define a Monad
    text
    copied!<p>This is a question relating to the API design practises for defining your own Monad instances for Haskell libraries. Defining Monad instances seems to be a good way to isolate DSL's e.g. <code>Par</code> monad in monad-par, hdph; <code>Process</code> in distributed-process; <code>Eval</code> in parallel etc...</p> <p>I take two examples of haskell libraries, whose purpose is to IO with database backends. The examples I take are <a href="http://hackage.haskell.org/package/riak" rel="nofollow">riak</a> for Riak IO, and <a href="http://hackage.haskell.org/package/hedis" rel="nofollow">hedis</a> for Redis IO.</p> <p>In hedis, a <code>Redis</code> monad <a href="http://hackage.haskell.org/packages/archive/hedis/0.6.2/doc/html/Database-Redis.html#t:Redis" rel="nofollow">is defined</a>. From there, you run IO with redis as:</p> <pre><code>data Redis a -- instance Monad Redis runRedis :: Connection -&gt; Redis a -&gt; IO a class Monad m =&gt; MonadRedis m class MonadRedis m =&gt; RedisCtx m f | m -&gt; f set :: RedisCtx m f =&gt; ByteString -&gt; ByteString -&gt; m (f Status) example = do conn &lt;- connect defaultConnectInfo runRedis conn $ do set "hello" "world" world &lt;- get "hello" liftIO $ print world </code></pre> <p>In riak, things are different:</p> <pre><code>create :: Client -&gt; Int -&gt; NominalDiffTime -&gt; Int -&gt; IO Pool ping :: Connection -&gt; IO () withConnection :: Pool -&gt; (Connection -&gt; IO a) -&gt; IO a example = do conn &lt;- connect defaultClient ping conn </code></pre> <p>The documentation for <code>runRedis</code> says: <em>"Each call of runRedis takes a network connection from the Connection pool and runs the given Redis action. Calls to runRedis may thus block while all connections from the pool are in use."</em>. However, the riak package also implements connection pools. This is done without additional monad instances on top of the IO monad:</p> <pre><code>create :: Client-&gt; Int -&gt; NominalDiffTime -&gt; Int -&gt; IO Pool withConnection :: Pool -&gt; (Connection -&gt; IO a) -&gt; IO a exampleWithPool = do pool &lt;- create defaultClient 1 0.5 1 withConnection pool $ \conn -&gt; ping conn </code></pre> <p>So, the analogy between the two packages boils down to these two functions:</p> <pre><code>runRedis :: Connection -&gt; Redis a -&gt; IO a withConnection :: Pool -&gt; (Connection -&gt; IO a) -&gt; IO a </code></pre> <p>As far as I can tell, the hedis package introduces a monad <code>Redis</code> to encapsulate IO actions with redis using <code>runRedis</code>. In contrast the riak package in <code>withConnection</code> simply takes a function that takes a <code>Connection</code>, and executes it in the IO monad.</p> <p>So, what are the motivations for defining your own Monad instances and Monad stacks? Why has the riak and redis packages differed in their approach to this?</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload