hypertypes-0.2.2: src/Hyper/Recurse.hs
{-# LANGUAGE FlexibleContexts #-}
-- | Combinators for processing/constructing trees recursively
module Hyper.Recurse
( module Hyper.Class.Recursive
, fold
, unfold
, wrap
, wrapM
, unwrap
, unwrapM
, foldMapRecursive
, HRecWitness (..)
, (#>>)
, (#**#)
, (##>>)
) where
import Hyper.Class.Foldable
import Hyper.Class.Functor (HFunctor (..))
import Hyper.Class.Nodes (HWitness, (#*#), (#>))
import Hyper.Class.Recursive
import Hyper.Class.Traversable
import Hyper.Type
import Hyper.Type.Pure (Pure (..), _Pure)
import Hyper.Internal.Prelude
-- | @HRecWitness h n@ is a witness that @n@ is a recursive node of @h@
data HRecWitness h n where
HRecSelf :: HRecWitness h h
HRecSub :: HWitness h c -> HRecWitness c n -> HRecWitness h n
-- | Monadically convert a 'Pure' to a different 'HyperType' from the bottom up
{-# INLINE wrapM #-}
wrapM ::
forall m h w.
(Monad m, RTraversable h) =>
(forall n. HRecWitness h n -> n # w -> m (w # n)) ->
Pure # h ->
m (w # h)
wrapM f x =
x ^. _Pure
& htraverse (Proxy @RTraversable #*# \w -> wrapM (f . HRecSub w))
>>= f HRecSelf
\\ recurse (Proxy @(RTraversable h))
-- | Monadically unwrap a tree from the top down, replacing its 'HyperType' with 'Pure'
{-# INLINE unwrapM #-}
unwrapM ::
forall m h w.
(Monad m, RTraversable h) =>
(forall n. HRecWitness h n -> w # n -> m (n # w)) ->
w # h ->
m (Pure # h)
unwrapM f x =
f HRecSelf x
>>= htraverse (Proxy @RTraversable #*# \w -> unwrapM (f . HRecSub w))
<&> (_Pure #)
\\ recurse (Proxy @(RTraversable h))
-- | Wrap a 'Pure' to a different 'HyperType' from the bottom up
{-# INLINE wrap #-}
wrap ::
forall h w.
Recursively HFunctor h =>
(forall n. HRecWitness h n -> n # w -> w # n) ->
Pure # h ->
w # h
wrap f x =
x ^. _Pure
& hmap (Proxy @(Recursively HFunctor) #*# \w -> wrap (f . HRecSub w))
& f HRecSelf
\\ recursively (Proxy @(HFunctor h))
-- | Unwrap a tree from the top down, replacing its 'HyperType' with 'Pure'
{-# INLINE unwrap #-}
unwrap ::
forall h w.
Recursively HFunctor h =>
(forall n. HRecWitness h n -> w # n -> n # w) ->
w # h ->
Pure # h
unwrap f x =
_Pure
# hmap
(Proxy @(Recursively HFunctor) #*# \w -> unwrap (f . HRecSub w))
(f HRecSelf x)
\\ recursively (Proxy @(HFunctor h))
-- | Recursively fold up a tree to produce a result (aka catamorphism)
{-# INLINE fold #-}
fold ::
Recursively HFunctor h =>
(forall n. HRecWitness h n -> n # Const a -> a) ->
Pure # h ->
a
fold f = getConst . wrap (fmap Const . f)
-- | Build/load a tree from a seed value (aka anamorphism)
{-# INLINE unfold #-}
unfold ::
Recursively HFunctor h =>
(forall n. HRecWitness h n -> a -> n # Const a) ->
a ->
Pure # h
unfold f = unwrap (fmap (. getConst) f) . Const
-- | Fold over all of the recursive child nodes of a tree in pre-order
{-# INLINE foldMapRecursive #-}
foldMapRecursive ::
forall h p a.
(Recursively HFoldable h, Recursively HFoldable p, Monoid a) =>
(forall n q. HRecWitness h n -> n # q -> a) ->
h # p ->
a
foldMapRecursive f x =
f HRecSelf x
<> hfoldMap
( Proxy @(Recursively HFoldable) #*#
\w ->
hfoldMap (Proxy @(Recursively HFoldable) #> foldMapRecursive (f . HRecSub w))
\\ recursively (Proxy @(HFoldable p))
)
x
\\ recursively (Proxy @(HFoldable h))
infixr 0 #>>
infixr 0 ##>>
infixr 0 #**#
-- | @Proxy @c #> r@ replaces a recursive witness parameter of @r@ with a constraint on the witnessed node
{-# INLINE (#>>) #-}
(#>>) ::
forall c h n r.
(Recursive c, c h, RNodes h) =>
Proxy c ->
(c n => r) ->
HRecWitness h n ->
r
(#>>) _ r HRecSelf = r
(#>>) p r (HRecSub w0 w1) =
(Proxy @RNodes #*# p #> (p #>> r) w1) w0
\\ recurse (Proxy @(RNodes h))
\\ recurse (Proxy @(c h))
-- | @Proxy @c #> r@ replaces a recursive witness parameter of @r@ with a @Recursively c@ constraint on the witnessed node
{-# INLINE (##>>) #-}
(##>>) ::
forall c h n r.
Recursively c h =>
Proxy c ->
(c n => r) ->
HRecWitness h n ->
r
(##>>) p r =
\case
HRecSelf -> r
HRecSub w0 w1 -> (Proxy @(Recursively c) #> (p ##>> r) w1) w0
\\ recursively (Proxy @(c h))
-- | A variant of '#>>' which does not consume the witness parameter.
--
-- @Proxy @c0 #**# Proxy @c1 #>> r@ brings into context both the @c0 n@ and @c1 n@ constraints.
{-# INLINE (#**#) #-}
(#**#) ::
(Recursive c, c h, RNodes h) =>
Proxy c ->
(c n => HRecWitness h n -> r) ->
HRecWitness h n ->
r
(#**#) p r w = (p #>> r) w w