Yesod generateFormPost - No instance for (RenderMessage master0 FormMessage) - haskell

I tried to follow this screencast by Michael Snoyman http://vimeo.com/39646807. However, it seems changes to the i18n causes that code to fail. I can't find any information on how to solve this problem on a scaffolded site and I can't quite make sense of the information given here http://www.yesodweb.com/book/internationalization.
This is the error i get, referring to code in Home.hs:
No instance for (RenderMessage master0 FormMessage)
arising from a use of `generateFormPost'
Possible fix:
add an instance declaration for (RenderMessage master0 FormMessage)
In a stmt of a 'do' block:
(formWidget, enctype) <- generateFormPost noteForm
In the expression:
do { (formWidget, enctype) <- generateFormPost noteForm;
defaultLayout ($(widgetFile "notes")) }
In an equation for `getNotesR':
getNotesR
= do { (formWidget, enctype) <- generateFormPost noteForm;
defaultLayout ($(widgetFile "notes")) }
The information seems pretty clear, the problem is I can't figure out how to add an instance declaration for (RenderMessage master0 FormMessage).
Here's the code I added to Home.hs
noteForm = renderBootstrap $ Note
<$> areq textField "Title" Nothing
<*> areq textField "Content" Nothing
getNotesR = do
(formWidget, enctype) <- generateFormPost noteForm
defaultLayout $(widgetFile "notes")
postNotesR = return ()
getNoteR noteId = return ()
Ant the following is from templates/notes.hamlet
<form method=post enctype=#{enctype}>
^{formWidget}
<input type=submit>

As a general proposition, when you see something like this:
No instance for (RenderMessage master0 FormMessage)
Bear in mind that master0 (or anything like it starting with a lowercase letter) is a free type variable that has not been instantiated to a concrete type. You can mentally replace it with a if that helps. So now we see that the message says there's no generic RenderMessage instance that is uniquely determined by FormMessage in the second parameter and an arbitrary type in the first parameter.
therefore, the usual way to fix this is to figure out what you want to instantiate the free type to, and to provide a type signature or other hint to fix it to that instantiation.
In this case, the type signature suggested by Michael Snoyman noteForm :: Form Note serves this purpose.

What about adding this code ?
instance RenderMessage [The name of your Yesod instance] FormMessage where
renderMessage _ _ = defaultFormMessage
See http://www.yesodweb.com/book/internationalization.

Related

Mystery of subsite types

I can not figure out what types should go in my Foundation.hs when implementing type classes from the authentication plugin / it's use of the auth subsite:
I can feel that I am very close, but I lack understanding. I am simply trying to use a different layout for the login/registration pages.
In Foundation.hs:
instance YesodAuthSimple App where
type AuthSimpleId App = UserId
...
-- Works
onRegisterSuccess :: YesodAuthSimple site => AuthHandler site Html
onRegisterSuccess = authLayout $ [whamlet|
$newline never
<div>
<h1>You Registered successfully.
<p>
Some text here.
|]
-- Works when I do not write a type signature
loginTemplate toParent mErr = $(widgetFile "authpartials/login")
-- Does not work with or without type signatures
customAuthLayout widget = do
master <- getYesod
mmsg <- getMessage
muser <- maybeAuthPair
mcurrentRoute <- getCurrentRoute
pc <- widgetToPageContent $ do
$(widgetFile "custom-auth-layout")
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
The 432:15 is referring to the widgetToPageContent call.
In the type class definition Simple.hs:
class (YesodAuth site, PathPiece (AuthSimpleId site)) => YesodAuthSimple site where
type AuthSimpleId site
...
customAuthLayout :: WidgetFor site () -> AuthHandler site Html
...
I pasted in the definition of customAuthLayout from defaultLayout from Foundation.hs
Here is the error I get from GHC:
Foundation.hs:432:15: error:
• Could not deduce: m ~ HandlerFor App
from the context: MonadAuthHandler App m
bound by the type signature for:
customAuthLayout :: WidgetFor App () -> AuthHandler App Html
at src/Foundation.hs:(427,5)-(434,79)
‘m’ is a rigid type variable bound by
the type signature for:
customAuthLayout :: WidgetFor App () -> AuthHandler App Html
at src/Foundation.hs:(427,5)-(434,79)
Expected type: m (PageContent (Route App))
Actual type: HandlerFor App (PageContent (Route App))
• In a stmt of a 'do' block:
pc <- widgetToPageContent
$ do (do do (asWidgetT GHC.Base.. toWidget)
((blaze-markup-0.8.2.2:Text.Blaze.Internal.preEscapedText
GHC.Base.. Data.Text.pack)
"<!-- custom-auth-layout -->
<body class="d-flex align-items-center bg-auth border-top border-top-2 border-primary">")
....)
In the expression:
do master <- getYesod
mmsg <- getMessage
muser <- maybeAuthPair
mcurrentRoute <- getCurrentRoute
....
In an equation for ‘customAuthLayout’:
customAuthLayout widget
= do master <- getYesod
mmsg <- getMessage
muser <- maybeAuthPair
....
|
432 | pc <- widgetToPageContent $ do
| ^^^^^^^^^^^^^^^^^^^^^^^^...
I have used this tutorial successfully for normal (non-subsite pages) https://ersocon.net/cookbooks/yesod/html-and-seo/custom-layouts
But I am getting tripped up by the subsite types. I have read Michael Snoyman's very good old blog post on subsite types but I cannot understand GHC's error message.
I suspect either the type signature in Simple.hs is wrong, or I am missing something from the function definition.
Try to add liftHandler before widgetToPageContent:
...
pc <- liftHandler $ widgetToPageContent $ do
$(widgetFile "custom-auth-layout")
...
Key lines in the error message are:
Could not deduce: m ~ HandlerFor App
...
Expected type: m (PageContent (Route App))
Actual type: HandlerFor App (PageContent (Route App))
It is basically telling us that it expected a more generic type m, but instead it got a HandlerFor App. So the solution is simply to lift the call to widgetToPageContent using the liftHandler function.
To elaborate further, if we look at the type signature of the function widgetToPageContent, we see that it returns HandlerFor site (PageContent (Route site)). In this case, site instantiates to App, and that is the HandlerFor App (PageContent (Route App)) you see in the error message.
Similarly, your customLayout function returns AuthHandler site Html. AuthHandler is just a type synonym that constrains site to a type equivalent to HandlerSite m which is also an instance of YesodAuth. This also resolves to App, and that is why we get MonadAuthHandler App m and m (PageContent (Route App)) in the error message.

Yesod fvInput can't match 'App' with 'Auth'

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

use runDB in a SubSite Yesod

i want create a post method in a SubSite to create a new entity, i have this AForm
demoForm :: RenderMessage master FormMessage => Maybe Demo -> AForm (HandlerT master IO) Demo
demoForm demo = Demo
<$> areq textField (fieldSettingsLabel ("fieldone"::T.Text)) (demoFieldOne <$> demo)
<*> areq intField (fieldSettingsLabel ("fieldone"::T.Text)) (demoFieldTwo <$> demo)
<*> areq boolField (fieldSettingsLabel ("fieldThree"::T.Text)) (demoFieldThree <$> demo)
<*> areq dayField (fieldSettingsLabel ("fieldFour"::T.Text)) (demoFieldFour <$> demo)
and the Post method:
postDemoNewR :: (Yesod master,RenderMessage master FormMessage) => HandlerT DemoCrud (HandlerT master IO) Html
postDemoNewR = do
tp <- getRouteToParent
((result,widget), encoding) <- lift $ runFormPost $ renderBootstrap3 BootstrapBasicForm $ demoForm Nothing
case result of
FormSuccess demo -> do
_ <- lift $ runDB $ insert demo
redirect DemoNewR
_ -> lift $ defaultLayout $ do
let actionR = DemoNewR
[whamlet|
<form method=post action=#{tp DemoNewR} encType=#{encoding}>
^{widget}
<button .btn .btn-default> default text create
|]
but have the following error
Could not deduce (YesodPersistBackend master
~ persistent-2.1.3:Database.Persist.Sql.Types.SqlBackend)
from the context (Yesod master, RenderMessage master FormMessage)
bound by the type signature for
postDemoNewR :: (Yesod master, RenderMessage master FormMessage) =>
I thing that I need add YesodPersist but i'm not sure how
You need to add the following constraint to the postDemoNewR declaration:
YesodPersist master => YesodPersistBackend master ~ SqlBackend => …
The first constraint tells master must have persistent abilities while the second constraint tells the backend used for persistent should be an SQL backend.
You can find something similar in this other question

Error when using options fields in Yesod subsite form

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

Yesod custom settings.yml field type mismatch

I'm trying to create a new field in my settings.yml file for my static file's locations (so I can change from a local subdirectory in development to a CDN in production), but I can't seem to get a basic "hello world" going. Here is my Settings.hs:
data Extra = Extra
{ extraCopyright :: Text
, extraAnalytics :: Maybe Text -- ^ Google Analytics
, extraStatic :: Text
} deriving Show
parseExtra :: DefaultEnv -> Object -> Parser Extra
parseExtra _ o = Extra
<$> o .: "copyright"
<*> o .:? "analytics"
<*> o .: "static"
And here is the relevent part of settings.yml:
Default: &defaults
host: "*4" # any IPv4 host
port: 3000
approot: "http://localhost:3000"
copyright: Insert copyright statement here
#analytics: UA-YOURCODE
static: "/static"
And lastly, the part that throws the error. I didn't really know how I should've gone about including it's value in a request, so I just threw the code in to be executed on each request:
getHomeR :: Handler Html
getHomeR = do
(formWidget, formEnctype) <- generateFormPost sampleForm
let submission = Nothing :: Maybe (FileInfo, Text)
handlerName = "getHomeR" :: Text
test = fmap extraStatic getExtra
defaultLayout $ do
aDomId <- newIdent
setTitle "Welcome To Yesod!"
$(widgetFile "homepage")
And I have the same line added on postHomeR aswell (test = fmap extraStatic getExtra). My hamlet:
<p>#{test}
And finally, the error thrown on the screen:
Handler/Home.hs:37:11:
No instance for (blaze-markup-0.5.1.5:Text.Blaze.ToMarkup
(HandlerT App IO Text))
arising from a use of `toHtml'
Possible fix:
add an instance declaration for
(blaze-markup-0.5.1.5:Text.Blaze.ToMarkup (HandlerT App IO Text))
In the first argument of `Yesod.Core.Widget.asWidgetT
. toWidget', namely
`toHtml test'
In a stmt of a 'do' block:
(Yesod.Core.Widget.asWidgetT . toWidget) (toHtml test)
In a stmt of a 'do' block:
do { (Yesod.Core.Widget.asWidgetT . toWidget)
((blaze-markup-0.5.1.5:Text.Blaze.Internal.preEscapedText
. Data.Text.pack)
"<h1>");
((Control.Monad.liftM (toHtml .) getMessageRender)
>>=
(\ urender_aaYh
-> (Yesod.Core.Widget.asWidgetT . toWidget)
(urender_aaYh MsgHello)));
(Yesod.Core.Widget.asWidgetT . toWidget)
((blaze-markup-0.5.1.5:Text.Blaze.Internal.preEscapedText
. Data.Text.pack)
"</h1>\
\<ol><li>Now that you have a working project you should use the Yesod book to learn more. You can also use this scaffolded site to explore some basic concepts.</li><li> This page was generated by the ");
(Yesod.Core.Widget.asWidgetT . toWidget) (toHtml handlerName);
.... }
Build failure, pausing...
Is there any way I can cast it? Is there any way I can make the variable get assigned and loaded during app startup, but be accessible throughout the requests as a constant value? Any help would be greatly appreciated!!
The problem is:
let test = fmap extraStatic getExtra
getExtra is a Handler action, so what you want is:
test <- fmap extraStatic getExtra

Resources