{-# LANGUAGE TypeFamilies, TemplateHaskell, KindSignatures, EmptyDataDecls #-}

{- |

Module      :  Type.Spine.Stage0
Copyright   :  (c) The University of Kansas 2011
License     :  BSD3

Maintainer  :  nicolas.frisby@gmail.com
Stability   :  experimental
Portability :  see LANGUAGE pragmas (... GHC)

Declares the type wrappers (mappings @k -> *) for 'allKinds' and then defines
functions for referencing them. Also provides a Template Haskell function for
generating the @Spine@ type instances for user-defined datatypes.

-}
module Type.Spine.Stage0 where

import Language.Haskell.TH

import Type.Spine.TH (liftNameG, tyConSignature)
import Type.Spine.Kinds (allKinds, declareK, nameK)

import Control.Monad ((<=<))


-- | The @Spine@ type family represents its argument as either a @TypeName@ or
-- an application via @(:@)@.
type family Spine t

-- | @TypeName@ represents an occurrence of totally unapplied type name.
data TypeName x



fmap concat $ mapM declareK allKinds

-- | @kNameG k@ returns the globally unique name (i.e. TH's @NameG@) of the
-- declared wrapper for types of kind @k@.
kNameG :: Kind -> Q Name
kNameG k = $(caseE [| k |] $ [
  let n = nameK k in
  match (let p StarK = conP 'StarK []
             p (ArrowK k1 k2) = conP 'ArrowK [p k1, p k2] in p k)
  (normalB $ [| return $(liftNameG n) |]) []
    | k <- allKinds ] ++
  [match wildP (normalB [| fail $ show k ++ " is not supported by type-kinds" |]) []])

-- | @kTypeG = conT <=< kNameG@.
kTypeG :: Kind -> Q Type
kTypeG = conT <=< kNameG




-- | @spineType n@ generates the @Spine@ instance for the type named @n@.
spineType :: Name -> Q [Dec]
spineType n = do
  (ks, k) <- tyConSignature n
  let kq = kTypeG $ foldr ArrowK k ks
      t = [t| $kq $(conT n) |]
  (:[]) `fmap` tySynInstD ''Spine [t] [t| TypeName $t |]