Using Yesod, I want to show the user’s profile name in the navigation bar of every page and want to link, with the ProfileId, to the profile-page of a user. config/models contains:
User
ident Text
password Text Maybe
UniqueUser ident
Profile
username Text
user UserId
UniqueProfile user
UniqueUsername username
Snippet from Foundation.hs:
defaultLayout widget = do
master <- getYesod
mmsg <- getMessage
maid <- maybeAuthId
With the user-id I can query for the profile, but it’s burried in Maybes.
This Stack Overflow question gives a good hint how to handle all those Maybes… but, with my little experience of Haskell, I’m still struggling with it.
I came up with:
mpid <- runMaybeT $ do
ouid <- MaybeT maybeAuthId
(Entity pid _) <- MaybeT . runDB . getBy $ UniqueProfile ouid
return pid
mprofilename <- runMaybeT $ do
ouid <- MaybeT maybeAuthId
(Entity _ p) <- MaybeT . runDB . getBy $ UniqueProfile ouid
return $ profileUsername p
This works, but is not optimal – repeating code and double database hits. How can I refactor this code?
I thought this would work:
(mpid, mprofilename) <- runMaybeT $ do
ouid <- MaybeT maybeAuthId
(Entity pid p) <- MaybeT . runDB . getBy $ UniqueProfile ouid
return (pid, profileUsername p)
but, alas, a no-go:
Foundation.hs:91:9:
Couldn't match expected type `Maybe (t0, Text)'
with actual type `(t1, t2)'
In the pattern: (mpid, mprofilename)
In a stmt of a 'do' block:
(mpid, mprofilename) <- runMaybeT
$ do { ouid <- MaybeT maybeAuthId;
(Entity pid p) <- MaybeT . runDB . getBy $ UniqueProfile ouid;
return (pid, profileUsername p) }
In the expression:
do { master <- getYesod;
mmsg <- getMessage;
maid <- maybeAuthId;
(mpid, mprofilename) <- runMaybeT
$ do { ouid <- MaybeT maybeAuthId;
(Entity pid p) <- MaybeT . runDB . getBy
$ UniqueProfile ouid;
.... };
.... }
I understand the error, but I don’t get it solved.
Enlighten me!
You are trying to bind the result as if it is a
(Maybe a, Maybe b), but it is actually aMaybe (a, b).You could convert it easily enough:
Then this should work: