module Impl.Utils where
import Language.Haskell.TH
import qualified Data.Map as Map

-- | Type level list append
type family (as :: [x]) ++ (bs :: [x]) :: [x] where
  '[] ++ bs = bs
  (a ': as) ++ bs = a ': (as ++ bs)

-- | Converts a variable number of arguments into curried form. Ex:
--
--  >>> :!kind '[Int,String,Double] :-> IO ()
--  Int -> String -> Double -> IO ()
infixr 0 :->
type family as :-> r where
  (a ': as) :-> r = a -> as :-> r
  '[] :-> r = r

data Method a = Required a | Optional a

methodsFor :: Name -- ^ Typeclass name
           -> TypeQ -- ^ @ :: [`Method` `Symbol`]@

{- | Retrieve the method names of a typeclass as a typelevel list of @Method Symbol@s.
     A good default for instantiating the 'Methods' type,
     which is robust against changing method names. -}
{-# DEPRECATED methodsFor "`reify` doesn't currently allow introspecting default definitions,\
   \ so they are always `Required`" #-}
methodsFor n = do
  ClassI (ClassD _ _ _ _ decs) _ <- reify n
  let methods = Map.elems $ foldr addDecl Map.empty decs
  return (typeList methods)
    where
      mkOptional = \case Required a -> Optional a; Optional a -> Optional a
      addDecl = \case
                SigD n _ty -> Map.insert n
                   $ Required (LitT (StrTyLit (nameBase n)))
                ValD (VarP n) _ _ -> Map.adjust mkOptional n
                FunD n _          -> Map.adjust mkOptional n
      typeList :: [Method Type] -> Type
      typeList = foldr (cons . methodType)  PromotedNilT where
        cons t = AppT (AppT PromotedConsT t)
        methodType = \case
          Required t -> ConT 'Required `AppT` t
          Optional t -> ConT 'Optional `AppT` t