content: {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE FlexibleInstances #-}
  
  -- | Classes applying on 'HyperType's recursively
  module Hyper.Class.Recursive
      ( Recursive (..)
      , Recursively (..)
      , RNodes (..)
      , RTraversable (..)
      , RecMethod
      , DefRecMethod
      , proxyArgument
      ) where
  
  import Hyper.Class.Foldable
  import Hyper.Class.Functor (HFunctor (..))
  import Hyper.Class.Nodes (HNodes (..))
  import Hyper.Class.Traversable
  import Hyper.Type
  import Hyper.Type.Pure (Pure (..))
  
  import Hyper.Internal.Prelude
  
  -- | A class of constraint constructors that apply to all recursive child nodes
  class Recursive c where
      -- | Lift a recursive constraint to the next layer
      recurse :: (HNodes h, c h) => proxy (c h) -> Dict (HNodesConstraint h c)
  
  type RecMethod c h = Proxy h -> Dict (HNodesConstraint h c)
  type DefRecMethod c h = HNodesConstraint h c => RecMethod c h
  
  -- | A class of 'HyperType's which recursively implement 'HNodes'
  class HNodes h => RNodes h where
      recursiveHNodes :: RecMethod RNodes h
      {-# INLINE recursiveHNodes #-}
      default recursiveHNodes :: DefRecMethod RNodes h
      recursiveHNodes _ = Dict
  
  instance RNodes Pure
  instance RNodes (Const a)
  
  -- | Helper Proxy combinator that is useful in many instances of 'Recursive'
  proxyArgument :: proxy (f h :: Constraint) -> Proxy (h :: HyperType)
  proxyArgument _ = Proxy
  
  instance Recursive RNodes where
      {-# INLINE recurse #-}
      recurse = recursiveHNodes . proxyArgument
  
  -- | A constraint lifted to apply recursively.
  --
  -- Note that in cases where a constraint has dependencies other than 'RNodes',
  -- one will want to create a class such as RTraversable to capture the dependencies,
  -- otherwise using it in class contexts will be quite unergonomic.
  class RNodes h => Recursively c h where
      recursively :: proxy (c h) -> Dict (c h, HNodesConstraint h (Recursively c))
      {-# INLINE recursively #-}
      default recursively ::
          (c h, HNodesConstraint h (Recursively c)) =>
          proxy (c h) ->
          Dict (c h, HNodesConstraint h (Recursively c))
      recursively _ = Dict
  
  instance Recursive (Recursively c) where
      {-# INLINE recurse #-}
      recurse p =
          Dict \\ recursively (p0 p)
          where
              p0 :: proxy (Recursively c h) -> Proxy (c h)
              p0 _ = Proxy
  
  instance c Pure => Recursively c Pure
  instance c (Const a) => Recursively c (Const a)
  
  -- | A class of 'HyperType's which recursively implement 'HTraversable'
  class (HTraversable h, Recursively HFunctor h, Recursively HFoldable h) => RTraversable h where
      recursiveHTraversable :: RecMethod RTraversable h
      {-# INLINE recursiveHTraversable #-}
      default recursiveHTraversable :: DefRecMethod RTraversable h
      recursiveHTraversable _ = Dict
  
  instance RTraversable Pure
  instance RTraversable (Const a)
  
  instance Recursive RTraversable where
      {-# INLINE recurse #-}
      recurse = recursiveHTraversable . proxyArgument
  