Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deep maybe stack with yesod

Tags:

haskell

yesod

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' -> ...
like image 412
Masse Avatar asked Aug 26 '12 14:08

Masse


3 Answers

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).

like image 161
dflemstr Avatar answered Sep 23 '22 22:09

dflemstr


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.

like image 40
Gabriella Gonzalez Avatar answered Sep 25 '22 22:09

Gabriella Gonzalez


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.

like image 34
Dan Burton Avatar answered Sep 22 '22 22:09

Dan Burton