Deep maybe stack with yesod

2019-04-07 19:19发布

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

标签: haskell yesod
3条回答
beautiful°
2楼-- · 2019-04-07 19:57

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.

查看更多
我只想做你的唯一
3楼-- · 2019-04-07 20:09

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.

查看更多
女痞
4楼-- · 2019-04-07 20:12

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

查看更多
登录 后发表回答