рефакторинг может быть сложен в Yesod

Используя Yesod, я хочу показать имя профиля пользователя на панели навигации каждой страницы и хочу связать с помощью ProfileId страницу профиля пользователя. config/models содержит:

User
    ident Text
    password Text Maybe
    UniqueUser ident
Profile
    username Text
    user UserId
    UniqueProfile user
    UniqueUsername username

Фрагмент из Foundation.hs:

defaultLayout widget = do
    master <- getYesod
    mmsg <- getMessage
    maid <- maybeAuthId

С идентификатором пользователя я могу запросить профиль, но он скрыт в Maybes. Этот вопрос о переполнении стека дает хороший совет, как справиться со всеми этими Maybes... но , с моим небольшим опытом работы с Haskell, я все еще борюсь с этим.

Я придумал:

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

Это работает, но не оптимально — повторяющийся код и двойное обращение к базе данных. Как я могу реорганизовать этот код?

Я думал, что это сработает:

(mpid, mprofilename) <- runMaybeT $ do
    ouid <- MaybeT maybeAuthId
    (Entity pid p) <- MaybeT . runDB . getBy $ UniqueProfile ouid
    return (pid, profileUsername p)

но, увы, нет:

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;
                                          .... };
           .... }

Я понимаю ошибку, но не могу ее решить.

Просветите меня!


person davidbe    schedule 21.11.2012    source источник


Ответы (1)


Вы пытаетесь связать результат, как будто это (Maybe a, Maybe b), но на самом деле это Maybe (a, b).

Вы можете легко преобразовать его:

unpairMaybe :: Maybe (a, b) -> (Maybe a, Maybe b)
unpairMaybe (Just (x, y)) = (Just x,  Just y)
unpairMaybe Nothing       = (Nothing, Nothing)

Тогда это должно работать:

(mpid, mprofilename) <- liftM unpairMaybe $ runMaybeT $ do
                ouid <- MaybeT maybeAuthId
                (Entity pid p) <- MaybeT . runDB . getBy $ UniqueProfile ouid
                return (pid, profileUsername p)
person dave4420    schedule 21.11.2012
comment
Это сработало, однако мне нужно было немного изменить его: unpairMaybe Just (x, y) = (Just x, Just y) нужны дополнительные скобки: unpairMaybe (Just(x,y)) = (Just x, Just y). - person davidbe; 22.11.2012
comment
Извините, наверное, я слишком быстро печатал. Я исправил эту линию сейчас. - person dave4420; 22.11.2012