{-# LANGUAGE CPP #-}
{-# LANGUAGE TemplateHaskell #-}
#ifndef MIN_VERSION_template_haskell
#define MIN_VERSION_template_haskell(x,y,z) 1
#endif
module Control.Lens.Internal.TupleIxedTH
( makeAllTupleIxed
) where
import Control.Applicative
import Data.Traversable (traverse)
import Language.Haskell.TH
import Control.Lens.Internal.TH
indexN, ixValueN, ixedN, ixN :: Name
indexN = mkName "Index"
ixValueN = mkName "IxValue"
ixedN = mkName "Ixed"
ixN = mkName "ix"
makeAllTupleIxed :: DecsQ
makeAllTupleIxed = fmap concat (traverse makeTupleIxed [2..9])
makeTupleIxed :: Int -> DecsQ
makeTupleIxed n = sequence [tupleIndex n, tupleIxValue n, tupleIxed n]
tupleIndex :: Int -> DecQ
tupleIndex n = tySynInstD' indexN [fullTupleT n] [t|Int|]
tupleIxValue :: Int -> DecQ
tupleIxValue n = tySynInstD' ixValueN [fullTupleT n] (head tupleVarTypes)
fullTupleT :: Int -> TypeQ
fullTupleT n = toTupleT (take n tupleVarTypes)
tupleIxed :: Int -> DecQ
tupleIxed n = instanceD (cxt eqs) (conT ixedN `appT` fullTupleT n) [funD ixN clauses]
where
ty0:tyN = take n tupleVarTypes
#if MIN_VERSION_template_haskell(2,10,0)
eqs = [AppT . AppT EqualityT <$> ty0 <*> ty | ty <- tyN]
#else
eqs = [ty0 `equalP` ty | ty <- tyN]
#endif
clauses = map nClause [0..n-1] ++ [otherClause]
nClause i = do
let iP = litP (integerL (fromIntegral i))
f <- newName "f"
let fP = varP f
fE = varE f
xs <- mapM newName (take n nameSource)
let xsP = map varP xs
xsE = map varE xs
xE = varE (xs !! i)
clause [iP, fP, toTupleP xsP]
(normalB [| fmap (\x -> $(toTupleE (replaceAt i [|x|] xsE))) ($fE $xE) |])
[]
otherClause = do
x <- newName "x"
clause [wildP, wildP, varP x] (normalB [|pure $(varE x)|]) []
tupleVarTypes :: [TypeQ]
tupleVarTypes = map (varT . mkName) nameSource
nameSource :: [String]
nameSource = [ a:n | n <- "" : map show [1 :: Int ..]
, a <- ['a'..'z']
]
replaceAt :: Int -> a -> [a] -> [a]
replaceAt 0 x (_:ys) = x : ys
replaceAt i x (y:ys) = y : replaceAt (i-1) x ys
replaceAt _ _ [] = error "replaceAt: index too large"