4

I'm relatively new to Haskell, and this is my first time working with monad transformers. I'd really appreciate some help.

runQuery :: Pipe -> Query -> ActionM (Either Failure [Document])
runQuery pipe query = access pipe master "nutrition" (find query >>= rest) 

main = do
pipe <- runIOE $ connect $ host "127.0.0.1"  

scotty 3000 $ do
post "/" $ do
        b <-  body
        let user :: Either String User = eitherDecode b
        case user of 
            Left err -> text . pack $ "Could not decode the user:" ++ err ++ ":\n" ++ (show b)
            Right u -> do 
            let query::Query = (select ["i_name" =: ["$in" =: map (unpack . name) (foods u)]] "stock_foods")
            results <- runQuery pipe query  
            ...

I'm trying to query a mongodb database under the scotty web framework, but I'm getting an error about MonadBaseControl. Do I really have to make an instance of this to connect to a database with scotty, and how would I go about doing it?

No instance for (MonadBaseControl
                   IO (scotty-0.7.2:Web.Scotty.Types.ActionT Text IO))
  arising from a use of `find'
Possible fix:
  add an instance declaration for
  (MonadBaseControl
     IO (scotty-0.7.2:Web.Scotty.Types.ActionT Text IO))
Craig
  • 255
  • 1
  • 6
  • 2
    You should include enough code (including all imports, and names of the packages you use from Hackage) in your question so that we can try compiling it ourselves. It makes it easier to debug and test for different solutions. I'm guessing that you can fix this with a simple `lift` in your `runQuery` but can't test it. – shang May 08 '14 at 06:11
  • I really appreciate that. It turns out I needed to `lift` the runQuery function and change it's return type from `ActionM (Either ...)` to `IO (Either ...)`. I think I'll be able to make more progress with monad transformers now! – Craig May 08 '14 at 11:15
  • If it helps, I'm going through something very similar: using Scotty with Persistent, and trying to figure out how to use transformers. There's some code here which seems to be working: https://github.com/stu-smith/Brandy/blob/master/src/Api/Users.hs – stusmith May 08 '14 at 13:46
  • (Forgot to say: I should probably be using `liftIO` instead of multiple `lift`s... there;s plenty more tidying for me to do, so please don't think the code I posted is sensible: I'm just posting it for interest). – stusmith May 08 '14 at 13:47
  • Thanks, it's always good to see some examples! – Craig May 08 '14 at 15:01
  • 2
    https://github.com/scotty-web/scotty/pull/82 It's a pull request to add MonadBaseControl instance for ActionT. – Sebastian Wagner May 08 '14 at 17:01
  • Thanks for putting that in. It turns out it's necessary for the `find` function: http://hackage.haskell.org/package/mongoDB-1.4.4/docs/Database-MongoDB-Query.html – Craig May 08 '14 at 22:42

1 Answers1

7

mongoDB is generic enough to work in any monad that is instance of MonadBaseControl IO and MonadIO.

For example, you can choose IO monad. In this case you need liftIO . runQuery inside scotty's action:

import Web.Scotty
import Database.MongoDB
import qualified Data.Text.Lazy as T
import Control.Monad.IO.Class

runQuery :: Pipe -> Query -> IO [Document]
runQuery pipe query = access pipe master "nutrition" (find query >>= rest) 

main = do
  pipe <- connect $ host "127.0.0.1"
  scotty 3000 $ do
    get "/" $ do
      res <- liftIO $ runQuery pipe (select [] "stock_foods")
      text $ T.pack $ show res

After @Sebastian Philipp added MonadBaseControl instance for Scotty.ActionT, there is no need to lift anything. You can transparently work with mongoDB form scotty. Just change type signature and drop liftIOs:

runQuery :: Pipe -> Query -> ActionM [Document]
...
    get "/" $ do
      res <- runQuery pipe (select [] "stock_foods")
      text $ T.pack $ show res
Community
  • 1
  • 1
max taldykin
  • 12,459
  • 5
  • 45
  • 64