I'm just starting Haskell web development using Spock, persistent and blaze-html.
In one of the routes I have, I want to load every row in my selected tables. I do something like this:
get ("/show/flight/" <//> (var :: Var Integer)) $ \f -> requireUser $ \(_, l) -> do
fs <- runSQL $ loadFlightInfos f
case fs of
[] -> blaze $ template False (showResultAlertBar False "Oops, something went wrong! Please try again.")
_ -> blaze $ template True (H.toHtml $ usersUsername l) loadFlightSeat
where
loadFlightSeat :: H.Html
loadFlightSeat =
forM_ fs $ \fs' -> do
sid <- runSQL $ getSeatIdByFlight fs' c
case sid of
Nothing -> H.div H.! A.class_ "alert alert-danger" $ "Oops, something went wrong! Please try again."
Just rid -> H.a H.! A.href (H.toValue $ "/flight/seat/" <> show c <> "/" <> show (fromIntegral $ (fromSqlKey . entityKey) sid)) H.! A.class_ "btn btn-theme" $ H.toHtml fs'
loadFlightInfos
has type:
Integer -> SqlPersistM [Entity Flight]
and getSeatIdByFlight:
T.Text -> Integer -> SqlPersistM (Maybe (Entity Flight))
I copied runSQL
from Spock's blog sample app, and it's something like this:
runSQL :: (HasSpock m, SpockConn m ~ SqlBackend) => SqlPersistT (NoLoggingT (ResourceT IO)) a -> m a
runSQL action = runQuery $ \conn -> runResourceT $ runNoLoggingT $ runSqlConn action conn
The type error I got:
Couldn't match expected type ‘SqlBackend’
with actual type ‘SpockConn Text.Blaze.Internal.MarkupM’
In the expression: runSQL
In a stmt of a 'do' block:
sid <- runSQL $ getSeatIdByFlight fs' c
I still don't understand this type error, because I know runSQL
is a wrapper from persistent to Spock and if I simply just want to output HTML, why can't it pass type checking?
How do I resolve this type error?
Disclaimer: I didn't run your code.
I know
runSQL
is a wrapper from persistent to Spock
Exactly, and there lies your issue. The type of runSQL
is:
runSQL :: (HasSpock m, SpockConn m ~ SqlBackend)
=> SqlPersistT (NoLoggingT (ResourceT IO)) a -> m a
The result type, (HasSpock m, SpockConn m ~ SqlBackend) => m a
, tells us that runSQL
gives a result in the Spock monad. Therefore, loadFlightSeat
should be a Spock monad computation as well. However, you gave it type H.Html
, which has nothing to do with the Spock monad. The issue will probably go away if you remove the mistaken type signature of loadFlightSeat
and adjust your usage of loadFlightSeat
accordingly:
flightSeat <- loadFlightSeat -- Returns an H.Html value in the Spock monad.
case fs of
[] -> blaze $ template False (showResultAlertBar False "Oops, something went wrong! Please try again.")
_ -> blaze $ template True (H.toHtml $ usersUsername l) flightSeat
P.S.: The type error you got...
Couldn't match expected type ‘SqlBackend’
with actual type ‘SpockConn Text.Blaze.Internal.MarkupM’
In the expression: runSQL
In a stmt of a 'do' block:
sid <- runSQL $ getSeatIdByFlight fs' c
...is unusually weird because H.Html
happens to be a synonym for MarkupM ()
, with MarkupM
being a monad defined by blaze
. As a consequence, the signature you gave to loadFlightSeat
leads the compiler to attempt matching Spock's monad with MarkupM
.