packages feed

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