Questa è una domanda relativa alle pratiche di progettazione dell'API per la definizione delle proprie istanze Monad per le librerie Haskell. Definire le istanze di Monad sembra essere un buon modo per isolare DSL, ad es. Par
monade in monad-par, hdph; Process
in processo distribuito; Eval
in parallelo ecc ...Quando (e quando no) per definire una Monad
Prendo due esempi di librerie haskell, il cui scopo è l'IO con i backend del database. Gli esempi che prendo sono riak per Riak IO e hedis per Redis IO.
In hedis, a Redis
monade . Da lì, si esegue IO con Redis come:
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
In Riak, le cose sono diverse:
create :: Client -> Int -> NominalDiffTime -> Int -> IO Pool
ping :: Connection -> IO()
withConnection :: Pool -> (Connection -> IO a) -> IO a
example = do
conn <- connect defaultClient
ping conn
La documentazione per runRedis
dice: "Ogni chiamata di runRedis prende una connessione di rete dalla connessione pool ed esegue l'azione Redis fornita. Le chiamate a runRedis possono quindi bloccarsi mentre tutte le connessioni dal pool sono in uso. ". Tuttavia, il pacchetto riak implementa anche i pool di connessione. Questo viene fatto senza istanze monade supplementari sulla parte superiore della monade 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
Così, l'analogia tra i due pacchetti si riduce a queste due funzioni:
runRedis :: Connection -> Redis a -> IO a
withConnection :: Pool -> (Connection -> IO a) -> IO a
Per quanto posso dire, il pacchetto hedis introduce una monad Redis
per incapsulare azioni IO con redis usando runRedis
. Al contrario, il pacchetto riak in withConnection
prende semplicemente una funzione che prende uno Connection
e lo esegue nella monade IO.
Quindi, quali sono le motivazioni per la definizione delle istanze Monad e degli stack Monad? Perché i pacchetti riak e redis differiscono nel loro approccio a questo?
Come contesto per le risposte - nel caso non sia ovvio, i tipi 'Redis a' e' Connection -> IO a' sono approssimativamente equivalenti. Quindi questa è essenzialmente una differenza estetica, paragonabile a 'env -> IO a' vs.' ReaderT env IO a'. –
Quindi ciò significa che forse nessuno dei due è corretto e 'Codensity IO Connection' era la monade che voleva da sempre. –