functor-friends-0.1.0.0: src/Data/Functor/Holey.hs
{-# LANGUAGE DeriveFunctor, TypeFamilies, PatternSynonyms, RankNTypes #-}
module Data.Functor.Holey
( -- * The @Holey@ type family
Holey
-- * Types
, HoleF(..)
-- ** Patterns for hiding @Fix@
, pattern Hole
, pattern Existing
-- * Utility functions
, whole
, plug
, punch
) where
import Data.Functor.Foldable (Fix(..), unfix, fold, unfold)
import Data.Functor.Decomposed
-- | @HoleF f@ transforms the functor @f@ to add "holes".
data HoleF f t
= HoleF -- ^ A hole.
| ExistingF (f t) -- ^ A normal value.
deriving (Eq, Ord, Functor)
pattern Hole = Fix HoleF
pattern Existing e = Fix (ExistingF e)
instance Decomposed HoleF where
fmap1 _ HoleF = HoleF
fmap1 f (ExistingF e) = ExistingF (f e)
-- | If @T@ is a type that is defined as a fixpoint, then
-- a @Holey T@ will be the type of "@T@s with holes".
type family Holey f
type instance Holey (Fix f) = Fix (HoleF f)
-- | Upgrade a value of type @T@ to a value of type
-- @T@-with-holes, without actually introducing any holes.
whole :: Functor f => Fix f -> Fix (HoleF f)
whole = unfold (ExistingF . unfix)
-- | Fill all holes in a data structure with the given value.
plug :: Functor f => Fix f -> Fix (HoleF f) -> Fix f
plug x = fold phi
where
phi HoleF = x
phi (ExistingF e) = Fix e
-- | Replace any substructure matching the predicate with a hole.
punch :: Functor f => (Fix f -> Bool) -> Fix f -> Fix (HoleF f)
punch test = unfold phi
where
phi x@(Fix e) = if test x then HoleF else ExistingF e