Dies ist eine Frage in Bezug auf die API-Design-Praktiken zum Definieren eigener Monad-Instanzen für Haskell-Bibliotheken. Definition von Monad-Instanzen scheint eine gute Möglichkeit zu sein, DSLs z.B. Par
monad in monad-par, hdph; Process
im verteilten Prozess; Eval
parallel usw.Wann (und wenn nicht) eine Monade definieren
Ich nehme zwei Beispiele für Haskell-Bibliotheken, deren Zweck es ist, IO mit Datenbank-Backends zu IO. Die Beispiele, die ich nehme, sind riak für Riak IO und hedis für Redis IO.
In hedis, ein Redis
monad . Von dort laufen Sie IO mit redis als:
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, sind die Dinge anders:
create :: Client -> Int -> NominalDiffTime -> Int -> IO Pool
ping :: Connection -> IO()
withConnection :: Pool -> (Connection -> IO a) -> IO a
example = do
conn <- connect defaultClient
ping conn
Die Dokumentation für runRedis
sagt: „Jeder Aufruf von runRedis eine Netzwerkverbindung aus der Verbindung erfolgt pool und führt die angegebene Redis-Aktion aus. Aufrufe von runRedis können daher blockieren, während alle Verbindungen aus dem Pool verwendet werden. ". Das Riak-Paket implementiert jedoch auch Verbindungspools. Dies wird ohne zusätzliche Monade Instanzen auf dem IO Monade getan:
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
So ist die Analogie zwischen den beiden Paketen läuft darauf hinaus, diese beiden Funktionen:
runRedis :: Connection -> Redis a -> IO a
withConnection :: Pool -> (Connection -> IO a) -> IO a
Soweit ich das beurteilen kann, Das Paket hedis führt eine Monade Redis
ein, um IO-Aktionen mit Redis unter Verwendung von runRedis
zu kapseln. Im Gegensatz dazu nimmt das Riak-Paket in withConnection
einfach eine Funktion an, die eine Connection
nimmt, und führt sie in der IO-Monade aus.
Also, was sind die Gründe für die Definition Ihrer eigenen Monad Instanzen und Monad Stacks? Warum unterscheiden sich die Riak- und Redis-Pakete in ihrem Ansatz?
Als Kontext für Antworten - falls es nicht offensichtlich ist, sind die Typen 'Redis a' und' Connection -> IO a' ungefähr gleichwertig. Dies ist also im Wesentlichen ein kosmetischer Unterschied, vergleichbar mit "env -> IO a" gegenüber "ReaderT env IO a". –
Dann heißt das vielleicht, dass keiner von beiden richtig ist und "Codesity IO Connection" die Monade war, die er die ganze Zeit haben wollte. –