packages feed

unification-fd-0.12.0: src/Data/Functor/Fixedpoint.hs

-- For 'build', 'hmap', and 'hmapM'
{-# LANGUAGE Rank2Types #-}

{-# OPTIONS_GHC -Wall -fwarn-tabs #-}
----------------------------------------------------------------
--                                                    2024-11-20
-- |
-- Module      :  Data.Functor.Fixedpoint
-- Copyright   :  Copyright (c) 2007--2024 wren gayle romano
-- License     :  BSD
-- Maintainer  :  wren@cpan.org
-- Stability   :  deprecated since unification-fd-0.12.0
-- Portability :  semi-portable (Rank2Types)
--
-- This module provides a backwards compatibility shim for users
-- of older versions of @unification-fd@, before we switched over
-- to using @data-fix@.  New users should prefer calling @data-fix@
-- functions directly, whenever possible.  If you use any of the
-- functions that aren't deprecated ('hoistFixM', 'ymap', 'ymapM',
-- 'ycata', 'ycataM', 'build'), please let the maintainer know,
-- so she can focus on getting those incorporated into @data-fix@.
-- Returning users should beware that this module used to provide
-- rewrite rules for fusing redundant traversals of data structures
-- (which @data-fix@ does not).  If you notice version >=0.12.0
-- introducing any performance loss compared to earlier versions,
-- please let the maintainer know, so she can focus on getting those
-- incorporated into @data-fix@.
--
-- This abstract nonsense is helpful in conjunction with other
-- category theoretic tricks like Swierstra's functor coproducts
-- (not provided by this package). For more on the utility of
-- two-level recursive types, see:
--
--     * Tim Sheard (2001) /Generic Unification via Two-Level Types/
--         /and Parameterized Modules/, Functional Pearl, ICFP.
--
--     * Tim Sheard & Emir Pasalic (2004) /Two-Level Types and/
--         /Parameterized Modules/. JFP 14(5): 547--587. This is
--         an expanded version of Sheard (2001) with new examples.
--
--     * Wouter Swierstra (2008) /Data types a la carte/, Functional
--         Pearl. JFP 18: 423--436.
----------------------------------------------------------------

module Data.Functor.Fixedpoint
    (
    -- * Fixed point operator for functors
      Data.Fix.Fix(..)
    -- * Maps
    , hmap,  hmapM, hoistFixM'
    , ymap,  ymapM
    -- * Builders
    , build
    -- * Catamorphisms
    , cata,  cataM
    , ycata, ycataM
    -- * Anamorphisms
    , ana,   anaM
    -- * Hylomorphisms
    , hylo,  hyloM
    ) where

import Prelude          hiding (mapM, sequence)
import Control.Monad    hiding (mapM, sequence)
import Data.Traversable
import Data.Fix (Fix())
import qualified Data.Fix

----------------------------------------------------------------
----------------------------------------------------------------

-- | A higher-order map taking a natural transformation @(f -> g)@
-- and lifting it to operate on @Fix@.
--
-- NOTE: The implementation of @hmap@ prior to version 0.12 was
-- based on 'ana', and therefore most closely matches the behavior
-- of 'Data.Fix.hoistFix''.  However, this definition is extensionally
-- equivalent to an implementation using 'cata' (and therefore most
-- closely matches the behavior of 'Data.Fix.hoistFix') instead.
hmap :: (Functor f, Functor g) => (forall a. f a -> g a) -> Fix f -> Fix g
hmap = Data.Fix.hoistFix'
{-# DEPRECATED hmap "Use Data.Fix.hoistFix'" #-}

-- | A monadic variant of 'hmap'.
hmapM
    :: (Functor f, Traversable g, Monad m)
    => (forall a. f a -> m (g a)) -> Fix f -> m (Fix g)
hmapM = hoistFixM'
{-# DEPRECATED hmapM "Use hoistFixM'" #-}

-- | A monadic variant of 'Data.Fix.hoistFix''.
--
-- NOTE: The implementation of @hmapM@ prior to version 0.12 was
-- based on 'anaM', and therefore most closely matches the behavior
-- of 'Data.Fix.unfoldFixM'. However, there is another function
-- of the same type which is instead implemented via 'cataM',
-- which has different semantics for many monads.
hoistFixM'
    :: (Functor f, Traversable g, Monad m)
    => (forall a. f a -> m (g a)) -> Fix f -> m (Fix g)
hoistFixM' eps = Data.Fix.unfoldFixM (eps . Data.Fix.unFix)

-- | A version of 'fmap' for endomorphisms on the fixed point. That
-- is, this maps the function over the first layer of recursive
-- structure.
ymap :: (Functor f) => (Fix f -> Fix f) -> Fix f -> Fix f
ymap f = Data.Fix.Fix . fmap f . Data.Fix.unFix

-- | A monadic variant of 'ymap'.
ymapM :: (Traversable f, Monad m)
      => (Fix f -> m (Fix f)) -> Fix f -> m (Fix f)
ymapM f = liftM Data.Fix.Fix . mapM f . Data.Fix.unFix


----------------------------------------------------------------
-- BUG: this isn't as helful as normal build\/fold fusion as in Data.Functor.Fusable
--
-- | Take a Church encoding of a fixed point into the data
-- representation of the fixed point.
build :: (Functor f) => (forall r. (f r -> r) -> r) -> Fix f
build g = g Data.Fix.Fix

----------------------------------------------------------------
-- | A pure catamorphism over the least fixed point of a functor.
-- This function applies the @f@-algebra from the bottom up over
-- @Fix f@ to create some residual value.
cata :: (Functor f) => (f a -> a) -> (Fix f -> a)
cata = Data.Fix.foldFix
{-# DEPRECATED cata "Use Data.Fix.foldFix" #-}


-- | A catamorphism for monadic @f@-algebras. Alas, this isn't wholly
-- generic to @Functor@ since it requires distribution of @f@ over
-- @m@ (provided by 'sequence' or 'mapM' in 'Traversable').
--
-- N.B., this orders the side effects from the bottom up.
cataM :: (Traversable f, Monad m) => (f a -> m a) -> (Fix f -> m a)
cataM = Data.Fix.foldFixM
{-# DEPRECATED cataM "Use Data.Fix.foldFixM" #-}

-- TODO: remove this, or add similar versions for ana* and hylo*?
-- | A variant of 'cata' which restricts the return type to being
-- a new fixpoint. Though more restrictive, it can be helpful when
-- you already have an algebra which expects the outermost @Fix@.
--
-- If you don't like either @fmap@ or @cata@, then maybe this is
-- what you were thinking?
ycata :: (Functor f) => (Fix f -> Fix f) -> Fix f -> Fix f
ycata f = Data.Fix.foldFix (f . Data.Fix.Fix)


-- TODO: remove this, or add similar versions for ana* and hylo*?
-- | Monadic variant of 'ycata'.
ycataM :: (Traversable f, Monad m)
       => (Fix f -> m (Fix f)) -> Fix f -> m (Fix f)
ycataM f = Data.Fix.foldFixM (f . Data.Fix.Fix)


----------------------------------------------------------------
-- | A pure anamorphism generating the greatest fixed point of a
-- functor. This function applies an @f@-coalgebra from the top
-- down to expand a seed into a @Fix f@.
ana :: (Functor f) => (a -> f a) -> (a -> Fix f)
ana = Data.Fix.unfoldFix
{-# DEPRECATED ana "Use Data.Fix.unfoldFix" #-}

-- | An anamorphism for monadic @f@-coalgebras. Alas, this isn't
-- wholly generic to @Functor@ since it requires distribution of
-- @f@ over @m@ (provided by 'sequence' or 'mapM' in 'Traversable').
--
-- N.B., this orders the side effects from the top down.
anaM :: (Traversable f, Monad m) => (a -> m (f a)) -> (a -> m (Fix f))
anaM = Data.Fix.unfoldFixM
{-# DEPRECATED anaM "Use Data.Fix.unfoldFixM" #-}


----------------------------------------------------------------
-- | @hylo phi psi == cata phi . ana psi@
hylo :: (Functor f) => (f b -> b) -> (a -> f a) -> (a -> b)
hylo = Data.Fix.refold
{-# DEPRECATED hylo "Use Data.Fix.refold" #-}

-- | @hyloM phiM psiM == cataM phiM <=< anaM psiM@
hyloM :: (Traversable f, Monad m)
      => (f b -> m b) -> (a -> m (f a)) -> (a -> m b)
hyloM = Data.Fix.refoldM
{-# DEPRECATED hyloM "Use Data.Fix.refoldM" #-}

----------------------------------------------------------------
----------------------------------------------------------- fin.