Haskell/Yesod - Butchering use of MaybeT
I'd suggest these points to simplify your code:


Make everything that returns Maybe something into MaybeT, and as close to the origin as possible. This will make composition of your functions much easier. For example:

lookupIdGetParam :: (PathPiece (Key a), MonadHandler m) =&gt; Text -&gt; MaybeT m (Key a)
lookupIdGetParam name = liftM fromPathPiece (MaybeT $ lookupGetParam name)


Notice using MaybeT just at lookupGetParam, then we don't need to duplicate MaybeT's functionality in the case expression.

This also helps you to avoid complicated code with two nested monads (such as SqlPersistT m (Maybe MarketingCampaigns)) and then complex and hard-to-read operations on them like fmap join (mapM ...).
MaybeT . fmap Just is equivalent to lift.
Whenever applicable, use functions from MonadPlus. Not just mzero (which is here equivalent to Nothing) and mplus, there are also some other very useful ones such as mfilter or guard. Like in:

fetchCampaigns :: MonadHandler m =&gt; Key User -&gt; Key Dealership -&gt; MaybeT (SqlPersistT m) MarketingCampaigns
fetchCampaigns userKey dealershipKey = do
  enabled &lt;- lift $ campaignsEnabled userKey dealershipKey
  guard enabled
  lift $ getCampaigns dealershipKey

Something to try out, I'm not sure if this will help you, but it might: Instead of a function that returns a Bool in a monad, return MaybeT MyMonad () such that original False corresponds to a failure in MaybeT and True to a () value. To be specific, instead of isDiscountEligible, you could have

checkDiscountElegible :: MonadHandler m =&gt; Key Vehicle -&gt; MaybeT (SqlPersistT m) ()
checkDiscountElegible vehicleKey = do
  vehicle &lt;- MaybeT (get vehicleKey)
  hasDiscount &lt;- vehicleHasDiscount &lt;$&gt; (MaybeT . get $ vehicleManufacturer vehicle)
                                    &lt;*&gt; pure vehicle
  guard hasDiscount


The idea is that this will make these functions composable with the other MaybeT ... ones, and avoid the tedious M.fromMaybe False &lt;$&gt; runMaybeT.

In other words, in MaybeT m Bool there are two failure modes: One failing within the monad, and another returning False. If you know from the beginning that they're both equivalent, just collapse them instead of keeping the distinction.


Disclaimer: I haven't tried to compile any of the above, so there might be syntax errors, but hopefully the meaning is clear.