I have an exposed function (a simple function with one Text argument, that performs IO operations and then returns a bool) in my subsite. But every time i call it from my main site boolVal <- Sub.getVal "abc" it throws the following error:
Couldn't match type ‘Sub’ with ‘App’
Expected type: HandlerT App IO Bool
Actual type: HandlerT Sub (HandlerT master0 IO) Bool
subsite code snippet
type SubHandler a = forall master. Yesod master => HandlerT Sub (HandlerT
master IO) a
getVal :: Text -> SubHandler Bool
getVal name = do
-- some operations including IO
return False
main site code snippet
import Sub
getHomeR :: Handler Html
getHomeR = do
boolVal <- Sub.getVal "abc"
defaultLayout $ do
$(widgetFile "home/index")
The subsite routes are working fine and i foolishly presumed the SubHandler would have registered with the main App, magically, like most things in haskell ha
thank you
FYI i would really appreciate a technical answer to this, and not just "the types don't match", albeit my understanding of classes and instances declarations is not all that great atm - work in progress :)
Related
I'm trying to write a custom Yesod registration form. The problem I'm having is that when it gets to fvInput it seems like it's using App instead of Auth. I'm not quite sure how this should be handled and I can't seem to find the terminology for it. I've tried lifting in various ways within the form and I can only get it to throw different errors. Also, the only time this error is thrown is if I have the fvInput line, but if I remove that no error is thrown and it compiles correctly.
Code:
registrationForm :: Html -> MForm (HandlerT Auth (HandlerT App IO)) (FormResult UserForm, Widget)
registrationForm extra = do
(emailRes, emailView) <- mreq textField "" Nothing
let userRes = UserForm <$> emailRes
let widget = do
[whamlet|
#{extra}
^{fvInput emailView}
<input type=Submit value="Registration">
|]
return (userRes, widget)
Error:
Foundation.hs:169:30:
Couldn't match type ‘App’ with ‘Auth’
In the second argument of ‘(GHC.Base..)’, namely ‘toWidget’
In the expression: asWidgetT GHC.Base.. toWidget
In a stmt of a 'do' block:
(asWidgetT GHC.Base.. toWidget) (fvInput emailView)
Thank you in advance for any help!
Edit
Error with a lift before the mreq:
Foundation.hs:166:49:
Couldn't match type ‘HandlerT Auth (HandlerT App IO)’
with ‘transformers-0.4.2.0:Control.Monad.Trans.RWS.Lazy.RWST
(Maybe (Env, FileEnv), HandlerSite m0, [Lang]) Enctype Ints m0’
Expected type: HandlerT
Auth (HandlerT App IO) (FormResult Text, FieldView App)
Actual type: MForm m0 (FormResult Text, FieldView App)
In the second argument of ‘($)’, namely ‘mreq textField "" Nothing’
In a stmt of a 'do' block:
(emailRes, emailView) <- lift $ mreq textField "" Nothing
Your returned Widget type is actually:
WidgetT App IO ()
Thus there's a mismatch between the Widget (which lives in just App) and the emailView (which lives in the lifted HandlerT Auth (HandlerT App IO) monad).
To solve this problem:
Change you type signature to registrationForm :: Html -> MForm (HandlerT App IO) (FormResult UserForm, Widget)
At the usage site, you'll need to employ a lift most likely, but after you've already called the appropriate run function for the form
I am following this tutorial http://www.parsonsmatt.org/programming/2015/06/07/servant-persistent.html to create APIs through servant. I want to customize the server to serve static files as well but couldn't find a way to do it.
I am using the stack build tool.
I modified the Main.hs file's run to include static (run port $ static $ logger $ app cfg) and I imported Network.Wai.Middleware.Static (static). I also added wai-middleware-static >=0.7.0 && < 0.71 to my cabal file.
When I run stack build I get: (Update: This part is totally my error. I added the the package to the wrong cabal file.. lame. Importing Network.Wai.Middleware.Static works and serves static files. Leaving the error below in case anyone searches for it and finds it useful.)
Could not find module ‘Network.Wai.Middleware.Static’
Perhaps you meant
Network.Wai.Middleware.Gzip (from wai-extra-3.0.7.1#waiex_GpotceEdscHD6hq9p0wPOJ)
Network.Wai.Middleware.Jsonp (from wai-extra-3.0.7.1#waiex_GpotceEdscHD6hq9p0wPOJ)
Network.Wai.Middleware.Local (from wai-extra-3.0.7.1#waiex_GpotceEdscHD6hq9p0wPOJ)
Next I tried using servant's serveDirectory as follows (simplified):
type API = "users" :> Get '[JSON] [Person]
:<|> "static" :> Raw
server = createPerson :<|> serveDirectory "/static"
I get this error:
Couldn't match type ‘IO’ with ‘EitherT ServantErr IO’
arising from a functional dependency between:
constraint ‘Servant.Server.Internal.Enter.Enter
(IO Network.Wai.Internal.ResponseReceived)
(AppM :~> EitherT ServantErr IO)
(IO Network.Wai.Internal.ResponseReceived)’
arising from a use of ‘enter’
instance ‘Servant.Server.Internal.Enter.Enter
(m a) (m :~> n) (n a)’
at <no location info>
In the expression: enter (readerToEither cfg) server
In an equation for ‘readerServer’:
readerServer cfg = enter (readerToEither cfg) server
I am a Haskell beginner and I am not familiar with Wai so unsure where to even begin. What changes do I need to make the example code in the Blog post to serve static files?
Edit: Since the comments get hidden from the default view, I am pasting my last comment here:
Here is toned down version of Matt's code from his blog. I consolidated all his modules into a single file, removed all the database stuff but did not clean up the extensions/imports. When I run this code I get the above type mismatch error. Please note that this code does not use Network.Wai.Middleware.Static and I am using qualified import of Servant StaticFiles.
As described in the relevant section of servant's tutorial, the whole deal with enter is to have your request handlers use some monad m (in your case some ReaderT monad) and to provide a way to convert a computation in m to a computation in servant's standard EitherT ServantErr IO monad.
The problem here though is that you define a bunch of request handlers in ReaderT and an additional one to serve static files, and call enter on all of these. The ReaderT handlers are converted to EitherT ... handlers just fine, but enter tries to convert the serveDirectory call from ReaderT ... to EitherT .... This is of course not going to happen anytime soon, since serveDirectory isn't a computation in ReaderT ... to begin with!
servant could arguably just leave serveDirectory alone -- at this point I don't have a definite opinion on whether we should do that or not, or if it's better to just have the file-serving handler be glued separately, to the result of calling enter on all the other endpoints. Here's how this would look like (look for -- NEW to see the changes):
type PersonAPI =
"users" :> Capture "name" String :> Get '[JSON] Person
-- NEW: removed Raw from here
-- NEW
type WholeAPI = PersonAPI :<|> Raw
type AppM = ReaderT Config (EitherT ServantErr IO)
userAPI :: Proxy PersonAPI
userAPI = Proxy
-- NEW
wholeAPI :: Proxy WholeAPI
wholeAPI = Proxy
-- NEW: changed 'userAPI' to 'wholeAPI'
app :: Config -> Application
app cfg = serve wholeAPI (readerServer cfg)
readerServer :: Config -> Server WholeAPI
readerServer cfg = enter (readerToEither cfg) server
:<|> S.serveDirectory "/static" -- NEW
readerToEither :: Config -> AppM :~> EitherT ServantErr IO
readerToEither cfg = Nat $ \x -> runReaderT x cfg
server :: ServerT PersonAPI AppM
server = singlePerson
singlePerson :: String -> AppM Person
singlePerson str = do
let person = Person { name = "Joe", email = "joe#example.com" }
return person
I have brought this topic to the attention of the other servant developers anyway, thanks! We hadn't really thought about the interaction between enter and serveDirectory so far (well, I did not).
I'm trying to use a selectFieldList inside a subsite form but I get the following error:
Couldn't match type 'IO' with 'HanderT master IO'
I'm running into this problem when using the following snippets, where the subsite is named TestSub (this subsite is separated from the master site):
Types:
type TestHandler a = forall master. Yesod master
=> HandlerT TestSub (HandlerT master IO) a
type Form a = forall master. Yesod master
=> Html -> MForm (HandlerT TestSub (HandlerT master IO)) (FormResult a, WidgetT TestSub IO ())
Form & handler:
testForm :: Form (Text, Int)
testForm = renderBootstrap3 BootstrapBasicForm $ (,)
<$> areq textField (bfs MsgText) Nothing
<*> areq (selectFieldList [(MsgFirst, 1), (MsgSecond, 2)]) (bfs MsgSelect) Nothing
getTestHome :: TestHandler Html
getTestHome = do
(formWidget, _) <- generateFormPost testForm
defaultLayoutSub $ do
setTitleI MsgTest
[whamlet|^{formWidget}|]
when the select field is replaced with for instance an intField the form and handler work as expected. While looking for the selectFieldList on Hoogle I found that options fields (select, radio, checkbox) have a different signature (displayed below) then the "normal" fields. I suspect this difference to be the problem but haven't found a work-around without having to implement the option fields all over.
Options field signature:
selectFieldList :: (Eq a, RenderMessage site FormMessage, RenderMessage site msg)
=> [(msg, a)]
-> Field (HandlerT site IO) a
Normal field signature:
intField :: (Monad m, Integral i, RenderMessage (HandlerSite m) FormMessage)
=> Field m i
Is there a way to get the options fields to work in a subsite context, without reimplementing them?
It's usually best to run your forms in the master site, not the subsite, by calling lift. You'll also need to modify your type synonyms a bit to match, but the basic idea is to replace:
(formWidget, _) <- generateFormPost testForm
with
(formWidget, _) <- lift $ generateFormPost testForm
EDIT
I still recommend the above approach. However, to get the alternate that you're asking for, change your type synonym to:
type Form a =
Html -> MForm (HandlerT HelloSub IO) (FormResult a, WidgetT HelloSub IO ())
and then use liftHandlerT:
liftHandlerT $ generateFormPost testForm
Keep in mind that this isn't how subsites are designed to be used, so you'll likely end up with some more friction as you keep going.
Regarding master translations: you can definitely leverage them, you just put in a constraint along the lines of RenderMessage master MessageDataType. That's what's used for FormMessage all over the place.
EDIT2
One more incantation you may find useful:
defaultLayoutSub $ liftWidgetT widget
I am toying around with the Snap framework and I often encounter
the case where I do a database lookup based on a parameter I get
from a form field.
Consider e.g. the following two functions
getParam :: (MonadSnap m) => ByteString -> m (Maybe ByteString)
doLookup :: (MonadIO (m b v), MonadSnaplet m, MonadState s (m b b), HasAcid s UrlDB) => ByteString -> m b v (EventResult QueryByURL)
where UrlDB is a mapping between Integers and URLs. The complicated type signature of the second
function is due to the use of acid-state and eventually results in Maybe Integer.
queryByURL :: Text -> Query UrlDB (Maybe Integer)
So far, my handler looks like
indexHandler :: Handler MyApp MyApp ()
indexHandler = do
mUrl <- getParam "url"
case mUrl of
Nothing -> render "index"
Just url -> do
mId <- doLookup $ url
case mId of
Nothing -> render "index"
Just i -> do
fancyStuffWith i
render "index"
Now, the first thing that annoys me is the staircasing of the case expressions.
The second thing is the threefold appearance of render "index".
Basically, whenever one of the two Maybe values is Nothing, I want to return
a default view.
What would be the cleanest way to do this?
This is what the MaybeT monad transformer is for. Your code could be written like this.
indexHandler :: Handler MyApp MyApp ()
indexHandler = do
runMaybeT $ do
url <- MaybeT $ getParam "url"
i <- MaybeT $ doLookup url
fancyStuffWith i
render "index"
The errors package pulls together these things and adds a lot of convenience functions for working with them. In addition to MaybeT, the EitherT monad transformer does something similar but keeps track of error messages so you can track when your computation failed.
To avoid repeating render "index", you have to see that you basically call it at the end of all code pathes. Then you can try to abstract that pattern matching part with a function. The result is not perfect but slightly better.
indexHandler :: Handler MyApp MyApp ()
indexHandler = do
withJust $ getParam "url" $ \url ->
withJust $ doLookup url $ fancyStuffWith
render "index"
where
withJust :: (IO (Maybe a)) -> (a -> IO()) -> IO ()
withJust iomval cmd = do
mval <- iomval
case mval of
Nothing -> return ()
Just val -> cmd val
the withJust function performs an IO action which might fail to bring a value. If it succeeds, the value is passed to another command.
this is probably a simple question and I've seen a similar one on SO, but I'm still stuck.
I'm trying to do an HTTP call to pull in the contents of another blog and display it on my page. This is more of a learning exercise than anything.
Here's my handler
blog :: App1Handler ()
blog = do
contents <- Requester.getUrl "http://someblog.com/"
heistLocal (bindString "contents" contents) . render $ "blog"
Requester.getUrl has the signature getUrl :: String -> IO T.Text
And the error I get back is
src/Main.hs:50:15:
Couldn't match expected type Handler App1 App1 t0' with actual typeIO T.Text'
In the return type of a call of `getUrl'
In a stmt of a 'do' block:
contents <- getUrl "http://someblog.com/"
In the expression:
do { contents <- getUrl "http://someblog.com/";
heistLocal (bindString "contents" contents) . render $ "blog" }
From what I gather, I'm stuck inside of the IO monad and it wants the type Handler App1 App1 t0. I've experimented with sticking liftIO in places, but I'm pretty confused on this one.
Can anyone point me in the right direction?
Thanks!
You just have to liftIO the IO action returned by getUrl, like this:
contents <- liftIO $ Requester.getUrl "http://someblog.com/"
The reasoning here is simple. You have a do-block of type App1Handler (), which means that the
right hand side of any <- statement within this do-block must have type App1Handler a.
However, getUrl returns IO Text, so you need to a function to convert from
IO a to App1Handler a which is exactly what liftIO does.
liftIO :: MonadIO m => IO a -> m a