Mystery of subsite types - haskell

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.

Related

Yesod Type Error - AuthId master vs. Key User [duplicate]

This question already has an answer here:
What's wrong with this YesodAuth instance?
(1 answer)
Closed 4 years ago.
I'm trying to add
instance YesodAuthEmail App
to the Yesod-Postgres scaffolding (yesod version 1.6) and getting stuck on a compile error.
The relevant code is:
instance YesodAuth App where
type AuthId App = UserId
....
authPlugins :: App -> [AuthPlugin App]
authPlugins app = [authOpenId Claimed []] ++ extraAuthPlugins
where extraAuthPlugins = [ authEmail ]
instance YesodAuthEmail App where
type AuthEmailId App = UserId
afterPasswordRoute _ = HomeR
addUnverified email verkey =
runDB $ insert $ User email Nothing
The error I receive is:
/home/justin/code/yesodemail/src/Foundation.hs:273:11: error:
• Could not deduce: m ~ HandlerFor site0 from the context: MonadAuthHandler App m
bound by the type signature for:
addUnverified :: Yesod.Auth.Email.Email -> VerKey -> AuthHandler App (AuthEmailId App)
....
Expected type: m (AuthEmailId App)
Actual type: HandlerFor site0 (Key User)
Based on the types,
getEmail :: AuthEmailId site -> AuthHandler site (Maybe Email)
type MonadAuthHandler master m = (MonadHandler m, YesodAuth master, master ~ HandlerSite m, Auth ~ SubHandlerSite m, MonadUnliftIO m)
type AuthHandler master a = forall m. MonadAuthHandler master m => m a
I would have thought this would compile. What I am misunderstanding?
P.S. I've tried to include everything relevant, but the full Foundation.hs is at https://gist.github.com/hyperpape/39d4d2baf67d3bdbdba45a943e7e0425
The type of runDB is:
runDB :: YesodDB site a -> HandlerFor site a
in order to call it in AuthHandler you need to lift it to HandlerFor.
If I am not mistaken this is what the liftHandler method from MonadHandler is for.
If you compose your runDB call with it, it should work:
addUnverified email verkey =
liftHandler . runDB $ insert $ User email Nothing
I found a detailed answer to your question here.

YesodAuthEmail could not deduce m ~ HandlerFor site0 [duplicate]

This question already has an answer here:
What's wrong with this YesodAuth instance?
(1 answer)
Closed 4 years ago.
I'm trying to add
instance YesodAuthEmail App
to the Yesod-Postgres scaffolding (yesod version 1.6) and getting stuck on a compile error.
The relevant code is:
instance YesodAuth App where
type AuthId App = UserId
....
authPlugins :: App -> [AuthPlugin App]
authPlugins app = [authOpenId Claimed []] ++ extraAuthPlugins
where extraAuthPlugins = [ authEmail ]
instance YesodAuthEmail App where
type AuthEmailId App = UserId
afterPasswordRoute _ = HomeR
addUnverified email verkey =
runDB $ insert $ User email Nothing
The error I receive is:
/home/justin/code/yesodemail/src/Foundation.hs:273:11: error:
• Could not deduce: m ~ HandlerFor site0 from the context: MonadAuthHandler App m
bound by the type signature for:
addUnverified :: Yesod.Auth.Email.Email -> VerKey -> AuthHandler App (AuthEmailId App)
....
Expected type: m (AuthEmailId App)
Actual type: HandlerFor site0 (Key User)
Based on the types,
getEmail :: AuthEmailId site -> AuthHandler site (Maybe Email)
type MonadAuthHandler master m = (MonadHandler m, YesodAuth master, master ~ HandlerSite m, Auth ~ SubHandlerSite m, MonadUnliftIO m)
type AuthHandler master a = forall m. MonadAuthHandler master m => m a
I would have thought this would compile. What I am misunderstanding?
P.S. I've tried to include everything relevant, but the full Foundation.hs is at https://gist.github.com/hyperpape/39d4d2baf67d3bdbdba45a943e7e0425
The type of runDB is:
runDB :: YesodDB site a -> HandlerFor site a
in order to call it in AuthHandler you need to lift it to HandlerFor.
If I am not mistaken this is what the liftHandler method from MonadHandler is for.
If you compose your runDB call with it, it should work:
addUnverified email verkey =
liftHandler . runDB $ insert $ User email Nothing
I found a detailed answer to your question here.

Haskell Yesod get user credentials in subsite

I'm writing a subsite for my yesod project, and I need to display the logged in user's name on that subsite (I'm using yesod-auth hardcoded where the type of AuthId master = Text).
However, the user is logged in on the master site. I'm able to get a value of type AuthId master using maybeAuthId, but I am unable to display this value because it is not an instance of Show.
Is there a type constraint I can put on my Handler to make sure the type of AuthId master derives Show?
getSubsiteHomeR :: (YesodAuth master) => HandlerT Subsite (HandlerT master IO) Html
getSubsiteHomeR = do
lift $ do
maid <- maybeAuthId -- I want to display the value of 'maid'
liftIO $ print maid
defaultLayout [whamlet|.......|]
EDIT: Here is the error message:
Could not deduce (Show (AuthId master))
arising from a use of `print'
from the context: YesodAuth master
bound by the type signature for:
getSubsiteHomeR :: YesodAuth master =>
HandlerT Subsite (HandlerT master IO) Html
at src/Subsite.hs:24:1-89
* In the second argument of `($)', namely `print maid'
In a stmt of a 'do' block: liftIO $ print maid
In the second argument of `($)', namely
`do { maid <- maybeAuthId;
liftIO $ print maid;
defaultLayout
(do { (asWidgetT . toWidget)
((blaze-markup-0.8.0.0:Text.Blaze.Internal.preEscapedText . T.pack)
"<p>Welcome to the Subsite!</br><a href=\"");
(getUrlRenderParams
>>=
(\ urender_alJ6
-> (asWidgetT . toWidget)
(toHtml
((\ u_alJ7 -> urender_alJ6 u_alJ7 ...)
(toParent SubsiteHomeR)))));
(asWidgetT . toWidget)
((blaze-markup-0.8.0.0:Text.Blaze.Internal.preEscapedText . T.pack)
"\">Subsite</br></a>\n\
\<a href=\"");
.... }) }'
Seems to me like all you need is a Show (AuthId master) constraint in your type signature:
getSubsiteHomeR :: (YesodAuth master, Show (AuthId master)) => HandlerT Subsite (HandlerT master IO) Html
Note that this requires the FlexibleContexts language extension, which you can enable by putting {-# LANGUAGE FlexibleContexts #-} at the top of your source file.

Yesod: Using Github API v3 Library for Haskell

I'm working on a project that builds on the simple yesod template. I am new to functional programming, haskell and Yesod so it's probably something obvious to anyone with Yesod experience. At the moment I am trying to make github API calls using this library. I am getting some type issues and I'm not even sure how to start approaching solving them.
You can find my handler here.
Handler/Home.hs:43:19:
Couldn't match expected type ‘HandlerT
App IO (Either a0 GitHub.User)’
with actual type ‘GitHub.Request k0 GitHub.User’
In a stmt of a 'do' block:
possibleUser <- GitHub.userInfoForR "mike-burns"
In the expression:
do { maid <- maybeAuthId;
possibleUser <- GitHub.userInfoForR "mike-burns";
result <- either (("Error: " <>) . tshow) formatUser possibleUser;
defaultLayout
(do { (asWidgetT GHC.Base.. toWidget)
((blaze-markup-0.7.1.1:Text.Blaze.Internal.preEscapedText
GHC.Base.. Data.Text.pack)
"<p>Your current auth ID: ");
(asWidgetT GHC.Base.. toWidget) (toHtml (show maid));
(asWidgetT GHC.Base.. toWidget)
((blaze-markup-0.7.1.1:Text.Blaze.Internal.preEscapedText
GHC.Base.. Data.Text.pack)
"</p>\n");
.... }) }
Handler/Home.hs:44:38:
Couldn't match type ‘Text’ with ‘HandlerT App IO a1’
Expected type: a0 -> HandlerT App IO a1
Actual type: a0 -> Text
In the second argument of ‘(.)’, namely ‘tshow’
In the first argument of ‘either’, namely
‘(("Error: " <>) . tshow)’
Handler/Home.hs:44:45:
Couldn't match type ‘Text’ with ‘HandlerT App IO a1’
Expected type: GitHub.User -> HandlerT App IO a1
Actual type: GitHub.User -> Text
In the second argument of ‘either’, namely ‘formatUser’
In a stmt of a 'do' block:
result <- either (("Error: " <>) . tshow) formatUser possibleUser
The GitHub library seems to be about building requests, and running them. The userInfoForR does such a thing :
userInfoForR :: Name User -> Request k User
Once you have a request, you can run it with one of the following functions, depending if you need to authenticate or not:
executeRequest :: Auth -> Request k a -> IO (Either Error a)
executeRequest' :: Request RO a -> IO (Either Error a)
I don't know about this specific case, but let's say you don't need authentication. So, the following expression would do the trick:
executeRequest' (userInfoForR "mike-burns") :: IO (Either Error User)
Now, in order to use it in a Handler, you'll need to learn about the fact that Handler is an instance of MonadIO, and you can thus do:
euser <- liftIO (executeRequest' (userInfoForR "mike-burns"))
case euser of
Left rr -> ...
Right user -> ...

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

Resources