module Control.Invertible.BiArrow.Free where
import Prelude hiding ((.), id, fst, snd, curry)
import Control.Invertible.BiArrow
import Control.Category
import Data.Groupoid
import Data.Invertible.Bijection
import Data.Invertible.Profunctor
import Data.Invertible.Strong
import Data.Semigroupoid
infixr :-
data FreeA p a b = PureP (a <-> b)
| forall x. p a x :- FreeA p x b
nil :: IsoProfunctor p => FreeA p a a
nil = PureP id
instance IsoProfunctor b => IsoProfunctor (FreeA b) where
lmap f (PureP g) = PureP (g . f)
lmap f (g :- h) = (lmap f g) :- h
rmap f (PureP g) = PureP (f . g)
rmap f (g :- h) = g :- (rmap f h)
instance IsoStrong p => IsoStrong (FreeA p) where
first' (PureP f) = PureP (first' f)
first' (g :- h) = (first' g) :- (first' h)
instance IsoProfunctor p => Semigroupoid (FreeA p) where
o g (PureP f) = lmap f g
o k (g :- h) = g :- (k `o` h)
instance IsoProfunctor p => Category (FreeA p) where
id = PureP id
(.) = o
revinv :: (IsoProfunctor p, Groupoid p)
=> (forall x y. p x y -> p y x)
-> FreeA p a b
-> FreeA p b a
revinv _ (PureP f) = PureP $ inv f
revinv i (g :- h) = go i h (i g :- nil)
go :: IsoProfunctor p => (forall x y. p x y -> p y x) -> FreeA p x d -> FreeA p x c -> FreeA p d c
go _ (PureP f) acc = lmap (inv f) acc
go i (g :- h) acc = go i h (i g :- acc)
instance (IsoProfunctor p, Groupoid p) => Groupoid (FreeA p) where
inv = revinv inv
instance (IsoProfunctor p, Groupoid p) => BiArrow (FreeA p) where
(<->) f g = PureP $ f :<->: g