Reputation: 4344
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' -> ...
Upvotes: 6
Views: 281
Reputation: 35089
From what I understand, your layers look like:
Maybe [Maybe r]
... and you want to join
the two Maybe
s 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 Just
s, 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 Maybe
s are Just
s 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.
Upvotes: 2
Reputation: 26147
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)
.
Upvotes: 6
Reputation: 53665
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.
Upvotes: 1