{-# LANGUAGE UndecidableInstances, TemplateHaskell, FlexibleInstances #-}

module AST.Term.FuncType
    ( FuncType(..), funcIn, funcOut, KWitness(..)
    , HasFuncType(..)
    ) where

import           AST
import           Control.DeepSeq (NFData)
import           Control.Lens (Prism', makeLenses)
import           Control.Lens.Operators
import           Data.Binary (Binary)
import           Generics.Constraints (makeDerivings, makeInstances)
import           GHC.Generics (Generic)
import           Text.PrettyPrint ((<+>))
import qualified Text.PrettyPrint as Pretty
import           Text.PrettyPrint.HughesPJClass (Pretty(..), maybeParens)
import           Text.Show.Combinators ((@|), showCon)

import           Prelude.Compat

-- | A term for the types of functions. Analogues to @(->)@ in Haskell.
--
-- @FuncType typ@s express types of functions of @typ@.
--
-- The data type comes along with the 'HasFuncType' class
-- for code to be able to work for any type AST supporting the types of functions.
data FuncType typ k = FuncType
    { _funcIn  :: k # typ
    , _funcOut :: k # typ
    } deriving Generic

makeLenses ''FuncType
makeZipMatch ''FuncType
makeKTraversableApplyAndBases ''FuncType
makeDerivings [''Eq, ''Ord] [''FuncType]
makeInstances [''Binary, ''NFData] [''FuncType]

instance Pretty (k # typ) => Pretty (FuncType typ k) where
    pPrintPrec lvl p (FuncType i o) =
        pPrintPrec lvl 11 i <+> Pretty.text "->" <+> pPrintPrec lvl 10 o
        & maybeParens (p > 10)

instance Show (k # typ) => Show (FuncType typ k) where
    showsPrec p (FuncType i o) = (showCon "FuncType" @| i @| o) p

-- | HasFuncType is a class of 'Knot's representing types that support the types of functions.
--
-- It is used by the 'AST.Class.Infer.Infer' instances of 'AST.Term.App.App' and 'AST.Term.Lam.Lam'
-- to work for any AST which provides 'HasFuncType'.
class HasFuncType typ where
    funcType :: Prism' (Tree typ k) (Tree (FuncType typ) k)