packages feed

traversal-template-0.0.0.0: src/Control/Traversal/TH.hs

{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
module TH.Traversal where

import Control.Monad
import Data.Bool
import Data.Foldable
import qualified Data.List as List
import Data.Traversable
import Language.Haskell.TH.Syntax
import Util ((∈))

makeTraversal :: Type -> Exp -> Name -> Q Exp
makeTraversal theType fExpr = reify >=> \ case
    TyConI (DataD _ _ [] _ cons _derivs) -> LamCaseE <$> for cons \ con ->
      [Match
       (ConP conName $ VarP . fst <$> xs)
       (NormalB $
        foldl' (AppE . AppE apExpr) (AppE pureExpr $ ConE conName)
        [AppE (bool pureExpr fExpr (theType ∈ typeHeads t)) (VarE v)
        | (v, t) <- xs]) []
      | (conName, xs) <- (traverse . traverse) ((<$> newName "a") . flip (,)) $
            let go = \ case
                    NormalC name bts -> (name, [t | (_, t) <- bts])
                    RecC name vbts -> (name, [t | (_, _, t) <- vbts])
                    InfixC lbt name rbt -> (name, [t | (_, t) <- [lbt, rbt]])
                    ForallC _ _ con -> go con
                    GadtC names bts _ -> (head names, [t | (_, t) <- bts])
                    RecGadtC names vbts _ -> (head names, [t | (_, _, t) <- vbts])
            in go con
      , pureExpr <- [|pure|], apExpr <- [|(<*>)|]
      ]
    info -> error $ show info

typeHeads :: Type -> [Type]
typeHeads = (:) <*> List.unfoldr \ case
    AppT t _ -> Just (t, t)
    _ -> Nothing