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' -> ...
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)
.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With