问题
I'm trying to set an authorization scheme where I check that 1. user is logged in 2. user has access to a certain object. For this I first call maybeAuthId, then try to get the current object, and 'join' to another table which lists permissions. There are two levels of maybe-cases and one level of empty-list case. I thought of using MaybeT, but either I'm too tired to get it to work or the "not really monad transformer"-handler-transformers can't be used with MaybeT. Is there a nice way to handle deep maybes?
Edit:
I was a bit unclear it seems. I meant that I have something like this:
case foo of
Nothing -> something
Just foo' -> do
bar <- somethingelse
case bar of
Nothing -> ...
Just bar' -> ...
回答1:
You can totally use MaybeT for Yesod. Just do it like this:
runMaybeT $ do
uid <- MaybeT maybeAuthID
car <- MaybeT . runDB . getBy $ UniqueCarOwner uid
location <- MaybeT . liftIO . ciaLocateLicensePlate . licensePlate $ car
country <- MaybeT . findCountry $ location
return (car, country)
As you said, most functions aren't optimized for generic error handling in Yesod. However, if you have something of the form Monad m => m (Maybe a), you can simply use MaybeT to turn it inside out into Monad m => Maybe (m a).
回答2:
From what I understand, your layers look like:
Maybe [Maybe r]
... and you want to join the two Maybes together, but the list is in the way. This is precisely the problem sequence solves:
sequence :: (Monad m) => [m r] -> m [r]
Note that if we specialize sequence to the Maybe monad we get:
sequence :: [Maybe r] -> Maybe [r]
In this particular case, sequence will return a Nothing if there is at least one Nothing in the list, but if they are all Justs, then it will join them all into a single Just.
All that remains is to map sequence over the outer Maybe:
fmap sequence :: Maybe [Maybe r] -> Maybe (Maybe [r])
Now this is exactly in the form we need for join:
join . fmap sequence :: Maybe [Maybe r] -> Maybe [r]
So, intuitively, what the above function does is that if all the inner Maybes are Justs and the outer Maybe is a Just, then it fuses the entire result into a single Just containing the list. However, if any Maybe (either the inner ones or outer one) is a Nothing, the final result is a Nothing.
Notice that despite doing a bunch of blind type-chasing, we ended up with a function that intuitively does the right thing. This is the power of abstractions grounded in category theory.
回答3:
It's not exactly clear what you mean by "handle deep maybes", but you can use monadic join (from Control.Monad) to remove one level of nesting at a time.
ghci> :m +Control.Monad
ghci> join (Just (Just 3))
Just 3
ghci> join (Just Nothing)
Nothing
ghci> join Nothing
Nothing
Using MaybeT is probably a better fit for your problem, though. If you clarify what you're trying to do, we could help you formulate it with MaybeT.
来源:https://stackoverflow.com/questions/12130732/deep-maybe-stack-with-yesod