-- | A 'HyperType' is a type parameterized by a hypertype.
--
-- This infinite definition is expressible using the 'AHyperType' 'Data.Kind.Kind' for hypertypes.
--
-- For more information see the [README](https://github.com/lamdu/hypertypes/blob/master/README.md).

module Hyper.Type
    ( HyperType
    , AHyperType(..), GetHyperType
    , type (#), type (:#)
    , asHyper
    ) where

import Data.Kind (Type)

import Prelude.Compat

-- | A hypertype is a type parameterized by a hypertype
type HyperType = AHyperType -> Type

-- | A 'Data.Kind.Kind' for 'HyperType's
newtype AHyperType = AHyperType HyperType

-- | A type-level getter for the type constructor encoded in 'AHyperType'.
--
-- Notes:
--
-- * If @DataKinds@ supported lifting field getters this would had been replaced with the type's getter.
-- * 'GetHyperType' is injective, but due to no support for constrained type families,
--   [that's not expressible at the moment](https://ghc.haskell.org/trac/ghc/ticket/15691).
-- * Because 'GetHyperType' can't declared as bijective, uses of it may restrict inference.
--   In those cases wrapping terms with the 'asHyper' helper assists Haskell's type inference
--   as if Haskell knew that 'GetHyperType' was bijective.
type family GetHyperType h where
    GetHyperType ('AHyperType t) = t

-- | A type synonym to express nested-HKD structures
type h # p = (h ('AHyperType p) :: Type)

-- | A type synonym to express child nodes in nested-HKDs
type h :# p = GetHyperType h # p

-- | An 'id' variant which tells the type checker that its argument is a hypertype.
--
-- See the notes for 'GetHyperType' which expand on why this might be used.
--
-- Note that 'asHyper' may often be used during development to assist the inference of incomplete code,
-- but removed once the code is complete.
asHyper :: h # p -> h # p
asHyper :: (h # p) -> h # p
asHyper = (h # p) -> h # p
forall a. a -> a
id