content: -- Exponential with GHC 8.10
  
  {-# LANGUAGE CPP #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module T13253 where
  
  import Control.Monad (liftM)
  import Control.Monad.Trans.RWS.Lazy -- check how strict behaves
  import Control.Monad.Trans.Reader (ReaderT)
  import Control.Monad.IO.Class (MonadIO (..))
  import Control.Monad.Trans.Class (MonadTrans (..))
  import Data.ByteString (ByteString)
  import Data.Monoid (Any (..))
  import Data.Semigroup (Semigroup (..))
  import Data.String (IsString (..))
  import System.Environment (getEnv)
  
  type Handler = ReaderT () IO
  type MForm = RWST (Maybe ([(String, Text)], ()), (), ()) Any [Int]
  type Text = ByteString -- close enough
  
  data HugeStruct = HugeStruct
    !Text
    !Text
    !Text
    !Text
    !Text
    !Text
    !Text
    !Text
    !Text -- 9th
    !Text
    !Text
  
  data FormResult a = FormMissing
                    | FormFailure [Text]
                    | FormSuccess a
      deriving Show
  instance Functor FormResult where
      fmap _ FormMissing = FormMissing
      fmap _ (FormFailure errs) = FormFailure errs
      fmap f (FormSuccess a) = FormSuccess $ f a
  instance Applicative FormResult where
      pure = FormSuccess
      (FormSuccess f) <*> (FormSuccess g) = FormSuccess $ f g
      (FormFailure x) <*> (FormFailure y) = FormFailure $ x ++ y
      (FormFailure x) <*> _ = FormFailure x
      _ <*> (FormFailure y) = FormFailure y
      _ <*> _ = FormMissing
  instance Monoid m => Monoid (FormResult m) where
      mempty = pure mempty
      mappend = (<>)
  instance Semigroup m => Semigroup (FormResult m) where
      x <> y = (<>) <$> x <*> y
  
  mreq :: MonadIO m => String -> MForm m (FormResult Text, ())
  -- fast
  --mreq v = pure (FormFailure [], ())
  -- slow
  mreq v = mhelper v (\m l -> FormFailure ["fail"]) FormSuccess
  
  askParams :: Monad m => MForm m (Maybe [(String, Text)])
  askParams = do
      (x, _, _) <- ask
      return $ liftM fst x
  
  mhelper
      :: MonadIO m
      => String
      -> (() -> () -> FormResult b) -- on missing
      -> (Text -> FormResult b)      -- on success
      -> MForm m (FormResult b, ())
  mhelper v onMissing onFound = do
      -- without tell, also faster
      tell (Any True)
      -- with different "askParams": faster.
      -- mp <- liftIO $ read <$> readFile v
      mp <- askParams
      (res, x) <- case mp of
          Nothing -> return (FormMissing, ())
          Just p -> do
              return $ case lookup v p of
                  Nothing -> (onMissing () (), ())
                  Just t -> (onFound t, ())
      return (res, x)
  
  -- not inlining, also faster:
  -- {-# NOINLINE mhelper #-}
  
  sampleForm2 :: MForm Handler (FormResult HugeStruct)
  sampleForm2 = do
      (x01, _) <- mreq "UNUSED"
      (x02, _) <- mreq "UNUSED"
      (x03, _) <- mreq "UNUSED"
      (x04, _) <- mreq "UNUSED"
      (x05, _) <- mreq "UNUSED"
      (x06, _) <- mreq "UNUSED"
      (x07, _) <- mreq "UNUSED"
      (x08, _) <- mreq "UNUSED"
      (x09, _) <- mreq "UNUSED"
      (x10, _) <- mreq "UNUSED"
      (x11, _) <- mreq "UNUSED"
  
      let hugeStructRes = HugeStruct
            <$> x01
            <*> x02
            <*> x03
            <*> x04
            <*> x05
            <*> x06
            <*> x07
            <*> x08
            <*> x09
            <*> x10
            <*> x11
  
      pure hugeStructRes
  
  
  main :: IO ()
  main = pure ()
  