I'm trying to write an apLogin for Yesod's Email Authentication plugin and form helpers are throwing a type error. The loginForm can't be read by runFormPost. I've tried several different combinations of type signatures, but this error message seems the clearest. It seems like I have the wrong top-level constraint, or that I'm missing one. What is the best way to set this up so that the proper types can be inferred?
authEmail :: (YesodAuthEmail m) => AuthPlugin m
authEmail =
AuthPlugin "email" dispatch login
where
login toParent =
toWidget $ do
((_,widget),enctype) <- runFormPost loginForm
[whamlet|
<form method="post" action="@{toParent loginR}">
|]
loginForm extra = do
let emailSettings = FieldSettings {
fsLabel = SomeMessage Msg.Email,
fsTooltip = Nothing,
fsId = Just "email",
fsName = Just "email",
fsAttrs = [("autofocus", "")]
}
(emailRes, emailView) <- mreq emailField emailSettings Nothing
let userRes = UserForm <$> emailRes
let widget = do
[whamlet|
#{extra}
^{fvInput emailView}
|]
return (userRes, widget)
This is the error:
Could not deduce (MonadIO m0) arising from a use of ‘loginForm’
from the context (YesodAuthEmail m)
bound by the type signature for
authEmail :: YesodAuthEmail m => AuthPlugin m
at Yesod/Auth/Email.hs:260:14-47
or from (MonadWidget m1, YesodAuth (HandlerSite m1))
bound by the inferred type of
login :: (MonadWidget m1, YesodAuth (HandlerSite m1)) =>
(AuthRoute -> Route (HandlerSite m1)) -> m1 ()
at Yesod/Auth/Email.hs:(270,5)-(275,14)
The type variable ‘m0’ is ambiguous
Note: there are several potential instances:
instance MonadIO m =>
MonadIO
(conduit-1.2.6.1:Data.Conduit.Internal.Conduit.ConduitM i o m)
-- Defined in ‘conduit-1.2.6.1:Data.Conduit.Internal.Conduit’
instance MonadIO m =>
MonadIO (conduit-1.2.6.1:Data.Conduit.Internal.Pipe.Pipe l i o u m)
-- Defined in ‘conduit-1.2.6.1:Data.Conduit.Internal.Pipe’
instance MonadIO IO -- Defined in ‘Control.Monad.IO.Class’
...plus 18 others
In the first argument of ‘runFormPost’, namely ‘loginForm’
In a stmt of a 'do' block:
((_, widget), enctype) <- runFormPost loginForm
In the second argument of ‘($)’, namely
‘do { ((_, widget), enctype) <- runFormPost loginForm;
(do { (asWidgetT . toWidget)
((Text.Blaze.Internal.preEscapedText . T.pack)
"<form method=\"post\" action=\"");
(getUrlRenderParams
>>=
(\ urender_ajDM
-> (asWidgetT . toWidget)
(toHtml (\ u_ajDN -> urender_ajDM u_ajDN ... (toParent loginR)))));
(asWidgetT . toWidget)
((Text.Blaze.Internal.preEscapedText . T.pack)
"\"></form>\n") }) }’
Code with handlerToWidget correction:
authEmail :: (YesodAuthEmail m) => AuthPlugin m
authEmail =
AuthPlugin "email" dispatch login
where
login toParent = do
((_,widget),enctype) <- handlerToWidget $ runFormPost loginForm
[whamlet|
<form method="post" action="@{toParent loginR}">
|]
loginForm extra = do
let emailSettings = FieldSettings {
fsLabel = SomeMessage Msg.Email,
fsTooltip = Nothing,
fsId = Just "email",
fsName = Just "email",
fsAttrs = [("autofocus", "")]
}
(emailRes, emailView) <- mreq emailField emailSettings Nothing
let userRes = UserForm <$> emailRes
let widget = do
[whamlet|
#{extra}
^{fvInput emailView}
|]
return (userRes, widget)
Error:
Could not deduce (MonadIO m0) arising from a use of ‘loginForm’
from the context (YesodAuthEmail m)
bound by the type signature for
authEmail :: YesodAuthEmail m => AuthPlugin m
at Yesod/Auth/Email.hs:260:14-47
or from (MonadIO m1,
MonadBaseControl IO m1,
exceptions-0.8.2.1:Control.Monad.Catch.MonadThrow m1,
YesodAuth site)
bound by the inferred type of
login :: (MonadIO m1, MonadBaseControl IO m1,
exceptions-0.8.2.1:Control.Monad.Catch.MonadThrow m1,
YesodAuth site) =>
(AuthRoute -> Route site) -> WidgetT site m1 ()
at Yesod/Auth/Email.hs:(270,5)-(275,14)
The type variable ‘m0’ is ambiguous
Note: there are several potential instances:
instance MonadIO m =>
MonadIO
(conduit-1.2.6.1:Data.Conduit.Internal.Conduit.ConduitM i o m)
-- Defined in ‘conduit-1.2.6.1:Data.Conduit.Internal.Conduit’
instance MonadIO m =>
MonadIO (conduit-1.2.6.1:Data.Conduit.Internal.Pipe.Pipe l i o u m)
-- Defined in ‘conduit-1.2.6.1:Data.Conduit.Internal.Pipe’
instance MonadIO IO -- Defined in ‘Control.Monad.IO.Class’
...plus 18 others
In the first argument of ‘runFormPost’, namely ‘loginForm’
In the second argument of ‘($)’, namely ‘runFormPost loginForm’
In a stmt of a 'do' block:
((_, widget), enctype) <- handlerToWidget $ runFormPost loginForm
Without toWidgetHandler error:
Could not deduce (MonadIO m0) arising from a use of ‘loginForm’
from the context (YesodAuthEmail m)
bound by the type signature for
authEmail :: YesodAuthEmail m => AuthPlugin m
at Yesod/Auth/Email.hs:260:14-47
or from (MonadIO m1,
MonadBaseControl IO m1,
exceptions-0.8.2.1:Control.Monad.Catch.MonadThrow m1,
YesodAuth site)
bound by the inferred type of
login :: (MonadIO m1, MonadBaseControl IO m1,
exceptions-0.8.2.1:Control.Monad.Catch.MonadThrow m1,
YesodAuth site) =>
(AuthRoute -> Route site) -> WidgetT site m1 ()
at Yesod/Auth/Email.hs:(271,5)-(275,10)
The type variable ‘m0’ is ambiguous
Note: there are several potential instances:
instance MonadIO m =>
MonadIO
(conduit-1.2.6.1:Data.Conduit.Internal.Conduit.ConduitM i o m)
-- Defined in ‘conduit-1.2.6.1:Data.Conduit.Internal.Conduit’
instance MonadIO m =>
MonadIO (conduit-1.2.6.1:Data.Conduit.Internal.Pipe.Pipe l i o u m)
-- Defined in ‘conduit-1.2.6.1:Data.Conduit.Internal.Pipe’
instance MonadIO IO -- Defined in ‘Control.Monad.IO.Class’
...plus 18 others
In the first argument of ‘runFormPost’, namely ‘loginForm’
In a stmt of a 'do' block:
((_, widget), enctype) <- runFormPost loginForm
In the expression:
do { ((_, widget), enctype) <- runFormPost loginForm;
(do { (asWidgetT . toWidget)
((Text.Blaze.Internal.preEscapedText . T.pack)
"<form method=\"post\" action=\"");
(getUrlRenderParams
>>=
(\ urender_ajDM
-> (asWidgetT . toWidget)
(toHtml (\ u_ajDN -> urender_ajDM u_ajDN ... (toParent loginR)))));
(asWidgetT . toWidget)
((Text.Blaze.Internal.preEscapedText . T.pack) "\"></form>\n") }) }
I'm not certain, but I think your
toWidget
call is your primary problem. Also, you'll need to lift yourrunFormPost
from Handler to Widget, which can be done withhandlerToWidget
.