{-# LANGUAGE ScopedTypeVariables #-}
module Data.Singletons.Deriving.Foldable where
import Data.Singletons.Deriving.Infer
import Data.Singletons.Deriving.Util
import Data.Singletons.Names
import Data.Singletons.Syntax
import Language.Haskell.TH.Desugar
mkFoldableInstance :: forall q. DsMonad q => DerivDesc q
mkFoldableInstance mb_ctxt ty dd@(DataDecl _ _ cons) = do
  functorLikeValidityChecks False dd
  f <- newUniqueName "_f"
  z <- newUniqueName "_z"
  let ft_foldMap :: FFoldType (q DExp)
      ft_foldMap = FT { ft_triv = mkSimpleLam $ \_ -> pure $ DVarE memptyName
                        
                      , ft_var = pure $ DVarE f
                        
                      , ft_ty_app = \_ g -> DAppE (DVarE foldMapName) <$> g
                        
                      , ft_forall  = \_ g -> g
                      , ft_bad_app = error "in other argument in ft_foldMap"
                      }
      ft_foldr :: FFoldType (q DExp)
      ft_foldr = FT { ft_triv = mkSimpleLam2 $ \_ z' -> pure z'
                      
                    , ft_var  = pure $ DVarE f
                      
                    , ft_ty_app = \_ g -> do
                        gg <- g
                        mkSimpleLam2 $ \x z' -> pure $
                          DVarE foldrName `DAppE` gg `DAppE` z' `DAppE` x
                      
                    , ft_forall  = \_ g -> g
                    , ft_bad_app = error "in other argument in ft_foldr"
                    }
      clause_for_foldMap :: [DPat] -> DCon -> [DExp] -> q DClause
      clause_for_foldMap = mkSimpleConClause $ \_ -> mkFoldMap
        where
          
          mkFoldMap :: [DExp] -> DExp
          mkFoldMap [] = DVarE memptyName
          mkFoldMap xs = foldr1 (\x y -> DVarE mappendName `DAppE` x `DAppE` y) xs
      clause_for_foldr :: [DPat] -> DCon -> [DExp] -> q DClause
      clause_for_foldr = mkSimpleConClause $ \_ -> mkFoldr
        where
          
          mkFoldr :: [DExp] -> DExp
          mkFoldr = foldr DAppE (DVarE z)
      mk_foldMap_clause :: DCon -> q DClause
      mk_foldMap_clause con = do
        parts <- foldDataConArgs ft_foldMap con
        clause_for_foldMap [DVarPa f] con =<< sequence parts
      mk_foldr_clause :: DCon -> q DClause
      mk_foldr_clause con = do
        parts <- foldDataConArgs ft_foldr con
        clause_for_foldr [DVarPa f, DVarPa z] con =<< sequence parts
      mk_foldMap :: q [DClause]
      mk_foldMap =
        case cons of
          [] -> pure [DClause [DWildPa, DWildPa] (DVarE memptyName)]
          _  -> traverse mk_foldMap_clause cons
      mk_foldr :: q [DClause]
      mk_foldr = traverse mk_foldr_clause cons
  foldMap_clauses <- mk_foldMap
  foldr_clauses   <- mk_foldr
  let meths = (foldMapName, UFunction foldMap_clauses)
              : case cons of
                  [] -> []
                  _  -> [(foldrName, UFunction foldr_clauses)]
  constraints <- inferConstraintsDef mb_ctxt (DConPr foldableName) ty cons
  return $ InstDecl { id_cxt = constraints
                    , id_name = foldableName
                    , id_arg_tys = [ty]
                    , id_sigs  = mempty
                    , id_meths = meths }