Search code examples
haskellhaskell-stack

Scotty: No instance for MonadIO ScottyT (arising from a use of ‘liftIO’)


I am trying to learn HASKELL by myself, I have found this page https://www.parsonsmatt.org/2015/05/02/scotty_and_persistent.html and I have tried to use the code but I got an error:

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Products
  name Text
  description Text
  price Int
  deriving Show
|]

main :: IO ()
main = scotty 3000 $ do
  Web.Scotty.middleware logStdoutDev
  inAppDb $ do
    doDbStuff
  Web.Scotty.get "/api/products" $ json [(0::Int)..10]

inAppDb = liftIO . dbFunction

dbFunction query = runStderrLoggingT $
        withPostgresqlPool connStr 10 $
        \pool -> liftIO $ runSqlPersistMPool query pool

doDbStuff = do
  res  :: [Entity Products] <- selectList [] [LimitTo 1]
  liftIO $ print res

Error

➜  my-project stack run
my-project> build (lib + exe)
Preprocessing library for my-project-0.1.0.0..
Building library for my-project-0.1.0.0..
ld: warning: -single_module is obsolete
Preprocessing executable 'my-project-exe' for my-project-0.1.0.0..
Building executable 'my-project-exe' for my-project-0.1.0.0..
[1 of 3] Compiling Main [Source file changed]

/Users/home/my-project/app/Main.hs:176:11: error:
    • No instance for (Control.Monad.IO.Class.MonadIO
                         (Web.Scotty.Internal.Types.ScottyT
                            Data.Text.Internal.Lazy.Text IO))
        arising from a use of ‘liftIO’
    • In the first argument of ‘(.)’, namely ‘liftIO’
      In the expression: liftIO . dbFunction
      In an equation for ‘inAppDb’: inAppDb = liftIO . dbFunction
    |
176 | inAppDb = liftIO . dbFunction
    |           ^^^^^^

Error: [S-7282]
       Stack failed to execute the build plan.

       While executing the build plan, Stack encountered the error:

       [S-7011]
       While building package my-project-0.1.0.0 (scroll up to its section to see the error) using:
       /Users/home/.stack/setup-exe-cache/aarch64-osx/Cabal-simple_6HauvNHV_3.8.1.0_ghc-9.4.7 --verbose=1 --builddir=.stack-work/dist/aarch64-osx/ghc-9.4.7 build lib:my-project exe:my-project-exe --ghc-options " -fdiagnostics-color=always"
       Process exited with code: ExitFailure 1

I do not know why the error is here: liftIO .

Do you have any ideas on it?

Thanks in advance

Aron


Solution

  • As per the comments, Scotty was redesigned starting with version 0.10 to make ScottyM (and ScottyT) into a "configuration only" monad that declares the middleware and routing of the web server but isn't capable of performing I/O itself.

    So, any database setup or other I/O that the blog author has tried to move into the Scotty app using inAppDb isn't going to work. Instead, delete the inAppDb definition and move that setup into main before invoking scotty. For your specific code example, it'll look like this:

    main :: IO ()
    main = do
      dbFunction $ do
        doDbStuff
      scotty 3000 $ do
        Web.Scotty.middleware logStdoutDev
        Web.Scotty.get "/api/products" $ json [(0::Int)..10]
    

    As far as the rest of the blog post is concerned, it looks like the author abandons the whole inAppDb by part 2 anyway, and the inHandlerDb calls should all work fine. They are operating in the ActionM monad, which supports I/O operations via liftIO, so examples like the following should typecheck:

    import qualified Web.Scotty as S
    import qualified Data.Text.Lazy as T
    
    inHandlerDb = liftIO . dbFunction
    
    main :: IO ()
    main = do
      dbFunction $ do
        doMigrations
        doDbStuff
      S.scotty 3000 $ do
            S.middleware logStdoutDev
            S.get "/" $ S.html "Hello World"
            S.get "/products" $ do
                products <- inHandlerDb $ selectList [] []
                S.html (T.pack $ show $ length (products :: [Entity Products]))