monadas leibniz educatina haskell scotty haskell-wai

haskell - leibniz - Scotty: grupo de conexión como lector de mónada



leibniz educatina (2)

Como ha aludido, la forma de hacerlo accesible es envolver sus cálculos en la mónada Reader o, probablemente, en el transformador ReaderT . Así que su función de run (cambiado ligeramente)

run :: Pool Pipe -> Action IO a -> IO (Either Failure a) run pool act = flip withResource (/x -> access x master db act) =<< pool

se convierte en

run :: Action IO a -> ReaderT (Pool Pipe) IO (Either Failure a) run act = do pool <- ask withResource pool (/x -> access x master db act)

¡Los cálculos dentro de un ReaderT rma pueden acceder al r usando ask y ReaderT aparentemente lo evoca de la nada! En realidad, la mónada ReaderT simplemente está ReaderT el Env durante todo el cómputo sin que usted tenga que preocuparse por ello.

Para ejecutar una acción ReaderT , utilice runReaderT :: ReaderT rma -> r -> ma . Así que llama a runReaderT en su función de nivel superior de runReaderT para proporcionar el Pool y runReaderT desenvolverá el entorno de ReaderT y le devolverá un valor en la mónada base.

Por ejemplo, para evaluar su función de run .

-- remember: run act :: ReaderT (Pool Pipe) IO (Either Failure a) runReaderT (run act) pool

pero no querría usar runReaderT en run , ya que probablemente es parte de un cálculo más amplio que también debería compartir el entorno ReaderT . Trate de evitar el uso de runReaderT en los runReaderT de "hoja", por lo general, debería llamarlo lo más alto posible en la lógica del programa.

EDITAR : La diferencia entre Reader y ReaderT es que Reader es una mónada, mientras que ReaderT es un transformador de mónada. Es decir, ReaderT agrega el comportamiento del Reader a otra mónada (o pila de transformadores de mónada). Si no está familiarizado con los transformadores de mónada, recomendaría los transformadores haskell del mundo real .

Tiene showJson pool ~ ActionM () y desea agregar un entorno de Reader con acceso a un Pool Pipe . En este caso, realmente necesita transformadores ActionT y ScottyT lugar de ReaderT para trabajar con las funciones del paquete scotty .

Tenga en cuenta que ActionM es un type ActionM = ActionT Text IO definido type ActionM = ActionT Text IO , de manera similar para ScottyM .

No tengo todas las bibliotecas necesarias instaladas, por lo que es posible que esto no sea una comprobación de tipo, pero debería darle la idea correcta.

basal :: ScottyT Text (ReaderT (Pool Pipe) IO) () basal = do middleware $ staticPolicy (...) get "/json" showJson showJson :: ActionT Text (ReaderT (Pool Pipe) IO) () showJson = do pool <- lift ask let run act = withResource pool (/p -> access p master "index act) d <- liftIO $ run $ fetch $ select [] "tables" text . TL.pack $ either (const "") show d

Hay billones de tutoriales de mónadas que incluyen al lector y parece todo claro cuando lees sobre esto. Pero cuando realmente necesitas escribir, se convierte en un asunto diferente.

Nunca he usado el Reader, nunca lo puse en práctica. Así que no sé cómo hacerlo, aunque lo leí.

Necesito implementar un grupo de conexión de base de datos simple en Scotty para que cada acción pueda usar el grupo. El grupo debe ser "global" y accesible por todas las funciones de acción. Leí que la forma de hacerlo es la mónada del lector. Si hay alguna otra forma por favor hágamelo saber.

¿Puede por favor ayudarme y mostrar cómo hacer esto con el Reader correctamente? Probablemente aprenderé más rápido si veo cómo se hace con mis propios ejemplos.

{-# LANGUAGE OverloadedStrings #-} module DB where import Data.Pool import Database.MongoDB -- Get data from config ip = "127.0.0.1" db = "index" --Create the connection pool pool :: IO (Pool Pipe) pool = createPool (runIOE $ connect $ host ip) close 1 300 5 -- Run a database action with connection pool run :: Action IO a -> IO (Either Failure a) run act = flip withResource (/x -> access x master db act) =<< pool

Así que lo anterior es simple. y quiero usar la función ''ejecutar'' en cada acción de Scotty para acceder al conjunto de conexiones de la base de datos. Ahora, la pregunta es cómo envolverla en la mónada Reader para que sea accesible a todas las funciones. Entiendo que la variable ''pool'' debe ser ''como global'' para todas las funciones de acción de Scotty.

Gracias.

ACTUALIZAR

Estoy actualizando la pregunta con el fragmento de código completo. Donde paso la variable ''pool'' por la cadena de funciones. Si alguien puede mostrar cómo cambiarlo para utilizar el lector de mónadas, por favor. No entiendo cómo hacerlo.

{-# LANGUAGE OverloadedStrings #-} module Main where import Network.HTTP.Types import Web.Scotty import qualified Data.Text as T import qualified Data.Text.Lazy as LT import Data.Text.Lazy.Internal import Data.Monoid (mconcat) import Data.Aeson (object, (.=), encode) import Network.Wai.Middleware.Static import Data.Pool import Database.MongoDB import Control.Monad.Trans (liftIO,lift) main = do -- Create connection pool to be accessible by all action functions pool <- createPool (runIOE $ connect $ host "127.0.0.1") close 1 300 5 scotty 3000 (basal pool) basal :: Pool Pipe -> ScottyM () basal pool = do middleware $ staticPolicy (noDots >-> addBase "static") get "/json" (showJson pool) showJson :: Pool Pipe -> ActionM () showJson pool = do let run act = withResource pool (/pipe -> access pipe master "index" act) d <- lift $ run $ fetch (select [] "tables") let r = either (const []) id d text $ LT.pack $ show r

Gracias.

ACTUALIZACIÓN 2

Traté de hacerlo de la manera que se sugirió a continuación, pero no funciona. Si alguien tiene alguna idea, por favor. La lista de errores de compilación es tan larga que ni siquiera sé por dónde empezar ...

main = do pool <- createPool (runIOE $ connect $ host "127.0.0.1") close 1 300 5 scotty 3000 $ runReaderT basal pool basal :: ScottyT LT.Text (ReaderT (Pool Pipe) IO) () basal = do middleware $ staticPolicy (noDots >-> addBase "static") get "/json" $ showJson showJson :: ActionT LT.Text (ReaderT (Pool Pipe) IO) () showJson = do p <- lift ask let rdb a = withResource p (/pipe -> access pipe master "index" a) j <- liftIO $ rdb $ fetch (select [] "tables") text $ LT.pack $ show j

ACTUALIZACIÓN 3

Gracias a cdk por dar la idea y gracias a Ivan Meredith por dar la sugerencia de ScottyT. Esta pregunta también ayudó: ¿Cómo agrego la mónada Reader a la mónada de Scotty? Esta es la versión que compila. Espero que ayude a alguien y le ahorre tiempo.

import qualified Data.Text.Lazy as T import qualified Data.Text.Lazy.Encoding as T import Data.Text.Lazy (Text) import Control.Monad.Reader import Web.Scotty.Trans import Data.Pool import Database.MongoDB type ScottyD = ScottyT Text (ReaderT (Pool Pipe) IO) type ActionD = ActionT Text (ReaderT (Pool Pipe) IO) -- Get data from config ip = "127.0.0.1" db = "basal" main = do pool <- createPool (runIOE $ connect $ host ip) close 1 300 5 let read = /r -> runReaderT r pool scottyT 3000 read read basal -- Application, meaddleware and routes basal :: ScottyD () basal = do get "/" shoot -- Route action handlers shoot :: ActionD () shoot = do r <- rundb $ fetch $ select [] "computers" html $ T.pack $ show r -- Database access shortcut rundb :: Action IO a -> ActionD (Either Failure a) rundb a = do pool <- lift ask liftIO $ withResource pool (/pipe -> access pipe master db a)


He estado tratando de resolver este problema exacto yo mismo. Gracias a las sugerencias sobre esta pregunta de SO, y otras investigaciones, he encontrado las siguientes que me funcionan. El bit clave que faltaba era usar scottyT

No hay duda de que hay una forma más bonita de escribir runDB, pero no tengo mucha experiencia en Haskell, así que por favor publíquela si puede hacerlo mejor.

type MCScottyM = ScottyT TL.Text (ReaderT (Pool Pipe) IO) type MCActionM = ActionT TL.Text (ReaderT (Pool Pipe) IO) main :: IO () main = do pool <- createPool (runIOE $ connect $ host "127.0.0.1") close 1 300 5 scottyT 3000 (f pool) (f pool) $ app where f = /p -> /r -> runReaderT r p app :: MCScottyM () app = do middleware $ staticPolicy (noDots >-> addBase "public") get "/" $ do p <- runDB dataSources html $ TL.pack $ show p runDB :: Action IO a -> MCActionM (Either Failure a) runDB a = (lift ask) >>= (/p -> liftIO $ withResource p (/pipe -> access pipe master "botland" a)) dataSources :: Action IO [Document] dataSources = rest =<< find (select [] "datasources")

Actualizar

Supongo que esto es un poco más bonito.

runDB :: Action IO a -> MCActionM (Either Failure a) runDB a = do p <- lift ask liftIO $ withResource p db where db pipe = access pipe master "botland" a