content: {-# LANGUAGE Safe #-}
  
  -- | This shows how `Data.Foldable.Foldable` is basically `Recursive`
  --   specialized to lists. The true operation of `Data.Foldable.Foldable` is
  --  `Data.Foldable.toList`.
  --
  --   As these few operations have the usual signatures, the rest of the type
  --   class can be implemented in the as in @base@.
  module Yaya.Experimental.Foldable
    ( Listable (naturalList),
      foldMap,
      foldl,
      foldr,
    )
  where
  
  import "base" Control.Category (Category (id, (.)))
  import "base" Data.Function (flip)
  import "base" Data.Monoid (Monoid)
  import "free" Control.Monad.Trans.Free (Free, iter)
  import "this" Yaya.Fold (Recursive (cata))
  import "this" Yaya.Fold.Common (lowerMonoid)
  import "this" Yaya.Pattern (XNor (Both, Neither))
  
  foldMap :: (Recursive (->) t (XNor a), Monoid m) => (a -> m) -> t -> m
  foldMap = cata . lowerMonoid
  
  -- | This class represents the ability of a structure to be converted to a
  --   list. It is equivalent to `Data.Foldable.Foldable`, but designed to
  --   illustrate the representation of `Data.Foldable.Foldable` as `Recursive`
  --   specialized to lists.
  class Listable f where
    naturalList :: f a b -> Free (XNor a) b
  
  -- toColist :: (Projectable t (f a), Corecursive (->) u (XNor a)) => t -> u
  -- toColist = elgotAna seqFree (naturalList . project)
  -- toList :: (Recursive (->) t (f a), Steppable u (XNor a)) => t -> u
  -- toList = cata (embed . unFree . naturalList)
  
  -- FIXME: Use @cata . liftCoEnv@  instead of `iter`.
  
  -- | This is simply `cata` applied to a list – the function is the @Cons@
  --   case, while the initial value is the @Nil@ case.
  foldr :: (Listable f, Recursive (->) t (f a)) => (a -> b -> b) -> b -> t -> b
  foldr f b =
    cata
      ( iter
          ( \case
              Neither -> b
              Both a r -> f a r
          )
          . naturalList
      )
  
  -- | Simply `cata` with a carrier of @b -> b@.
  foldl :: (Listable f, Recursive (->) t (f a)) => (b -> a -> b) -> b -> t -> b
  foldl f =
    flip
      ( cata
          ( iter
              ( \case
                  Neither -> id
                  Both a g -> g . flip f a
              )
              . naturalList
          )
      )
  