{-# LANGUAGE TemplateHaskell, ViewPatterns #-}
module Data.Tuple.Template where

import Control.Monad
import Language.Haskell.TH hiding ( tupleT )

-- | $(tupleName n) = Tuple[n]
tupleName :: Int -> Name
tupleName n = mkName $ "(" ++ replicate (n-1) ',' ++ ")"

tupleT :: [TypeQ] -> TypeQ
tupleT args = appsT (conT (tupleName n)) args
  where
    appsT = foldl appT
    n = length args

tupleE :: [ExpQ] -> ExpQ
tupleE args = appsE $ conE (tupleName n) : args
  where n = length args

tupleP :: [PatQ] -> PatQ
tupleP ps = conP (tupleName n) ps
  where n = length ps

decTupleCons :: Int -> Q Dec
decTupleCons n = do
  aas@(a : as) <- replicateM n (varT <$> newName "a")
  instanceD (cxt [])
    (foldl appT (conT (mkName "TupleCons"))
      [tupleT as])
    [typeD aas, consInlD, consD, splitInlD, splitD]
  where
    typeD aas@(a : as) =
      TySynInstD (mkName ":|") <$> tySynEqn [a, tupleT as] (tupleT aas)
    consInlD = pragInlD (mkName "|:|") Inline FunLike AllPhases
    splitInlD = pragInlD (mkName "split") Inline FunLike AllPhases
    consD = do
      xxs@(x : xs) <- replicateM n (newName "x")
      funD (mkName "|:|")
        [ clause
          [ varP x, tupleP (varP <$> xs) ]
          (normalB [| $(tupleE' xxs) |])
          [] ]
    splitD = do
      xxs@(x : xs) <- replicateM n (newName "x")
      funD (mkName "split")
        [ clause
          [ tupleP (varP <$> xxs) ]
          (normalB [| ($(varE x), $(tupleE' xs)) |])
          [] ]
    tupleE' = tupleE . fmap varE