bowtie-0.4.0: src/Bowtie/Knot.hs
{-# LANGUAGE UndecidableInstances #-}
module Bowtie.Knot
( Knot (..)
, mkKnot
, unMkKnot
, transKnot
)
where
import Bowtie.Foldable (Base1, Corecursive1 (..), Recursive1 (..), cata1, fmapViaBi, foldrViaBi, traverseViaBi)
import Data.Bifoldable (Bifoldable (..))
import Data.Bifunctor (Bifunctor (..))
import Data.Bitraversable (Bitraversable (..))
import Data.Kind (Type)
import Data.String (IsString (..))
import Prettyprinter (Pretty (..))
-- | A fixpoint for a Bifunctor where the second type variable contains
-- the recursive structure.
type Knot :: (Type -> Type -> Type) -> Type -> Type
newtype Knot g a = Knot {unKnot :: g a (Knot g a)}
deriving newtype instance (Eq (g a (Knot g a))) => Eq (Knot g a)
deriving newtype instance (Ord (g a (Knot g a))) => Ord (Knot g a)
deriving stock instance (Show (g a (Knot g a))) => Show (Knot g a)
deriving newtype instance (Pretty (g a (Knot g a))) => Pretty (Knot g a)
deriving newtype instance (IsString (g a (Knot g a))) => IsString (Knot g a)
type instance Base1 (Knot g) = g
instance (Bifunctor g) => Recursive1 (Knot g) where project1 = unKnot
instance (Bifunctor g) => Corecursive1 (Knot g) where embed1 = Knot
instance (Bifunctor g) => Functor (Knot g) where fmap = fmapViaBi
instance (Bifunctor g, Bifoldable g) => Foldable (Knot g) where foldr = foldrViaBi
instance (Bitraversable g) => Traversable (Knot g) where traverse = traverseViaBi
-- | Pull a recursive structure apart and retie as a 'Knot'.
mkKnot :: (Recursive1 f, Base1 f ~ g) => f a -> Knot g a
mkKnot = cata1 Knot
-- | Go the other way.
unMkKnot :: (Corecursive1 f, Base1 f ~ g) => Knot g a -> f a
unMkKnot = cata1 embed1
-- | Transform the base Bifunctor.
transKnot :: (Bifunctor g) => (forall x y. g x y -> h x y) -> Knot g a -> Knot h a
transKnot nat = go
where
go = Knot . nat . second go . unKnot