hypertypes-0.1.0.1: src/Hyper/Diff.hs
{-# LANGUAGE TemplateHaskell, FlexibleContexts, UndecidableInstances #-}
module Hyper.Diff
( diff
, Diff(..), _CommonBody, _CommonSubTree, _Different
, CommonBody(..), anns, val
, foldDiffs
, diffP
, DiffP(..), _CommonBodyP, _CommonSubTreeP, _DifferentP
, foldDiffsP
) where
import Hyper
import Hyper.Class.ZipMatch (ZipMatch(..))
import Hyper.Internal.Prelude
import Hyper.Recurse
-- | A 'HyperType' which represents the difference between two annotated trees.
-- The annotation types also function as tokens
-- to describe which of the two trees a term comes from.
data Diff a b e
= CommonSubTree (Ann (a :*: b) e)
| CommonBody (CommonBody a b e)
| Different ((Ann a :*: Ann b) e)
deriving Generic
-- | A 'HyperType' which represents two trees which have the same top-level node,
-- but their children may differ.
data CommonBody a b e = MkCommonBody
{ _anns :: (a :*: b) e
, _val :: e :# Diff a b
} deriving Generic
makePrisms ''Diff
makeLenses ''CommonBody
-- | Compute the difference of two annotated trees.
diff ::
forall t a b.
(Recursively ZipMatch t, RTraversable t) =>
Ann a # t -> Ann b # t -> Diff a b # t
diff x@(Ann xA xB) y@(Ann yA yB) =
withDict (recursively (Proxy @(ZipMatch t))) $
withDict (recurse (Proxy @(RTraversable t))) $
case zipMatch xB yB of
Nothing -> Different (x :*: y)
Just match ->
case htraverse (const (^? _CommonSubTree)) sub of
Nothing -> MkCommonBody (xA :*: yA) sub & CommonBody
Just r -> Ann (xA :*: yA) r & CommonSubTree
where
sub =
hmap
( Proxy @(Recursively ZipMatch) #*# Proxy @RTraversable #>
\(xC :*: yC) -> diff xC yC
) match
foldDiffs ::
forall r h a b.
(Monoid r, Recursively HFoldable h) =>
(forall n. HRecWitness h n -> Ann a # n -> Ann b # n -> r) ->
Diff a b # h ->
r
foldDiffs _ CommonSubTree{} = mempty
foldDiffs f (Different (x :*: y)) = f HRecSelf x y
foldDiffs f (CommonBody (MkCommonBody _ x)) =
withDict (recursively (Proxy @(HFoldable h))) $
hfoldMap
( Proxy @(Recursively HFoldable) #*#
\w -> foldDiffs (f . HRecSub w)
) x
data DiffP h
= CommonSubTreeP (HPlain (GetHyperType h))
| CommonBodyP (h :# DiffP)
| DifferentP (HPlain (GetHyperType h)) (HPlain (GetHyperType h))
deriving Generic
makePrisms ''DiffP
diffP ::
forall h.
(Recursively ZipMatch h, Recursively HasHPlain h, RTraversable h) =>
HPlain h -> HPlain h -> DiffP # h
diffP x y =
withDict (recursively (Proxy @(HasHPlain h))) $
diffPH (x ^. hPlain) (y ^. hPlain)
diffPH ::
forall h.
(Recursively ZipMatch h, Recursively HasHPlain h, RTraversable h) =>
Pure # h -> Pure # h -> DiffP # h
diffPH x y =
withDict (recursively (Proxy @(ZipMatch h))) $
withDict (recursively (Proxy @(HasHPlain h))) $
withDict (recurse (Proxy @(RTraversable h))) $
case zipMatch (x ^. _Pure) (y ^. _Pure) of
Nothing -> DifferentP (hPlain # x) (hPlain # y)
Just match ->
case htraverse_ (const ((() <$) . (^? _CommonSubTreeP))) sub of
Nothing -> CommonBodyP sub
Just () -> _CommonSubTreeP . hPlain # x
where
sub =
hmap
( Proxy @(Recursively ZipMatch) #*#
Proxy @(Recursively HasHPlain) #*#
Proxy @RTraversable #>
\(xC :*: yC) -> diffPH xC yC
) match
makeCommonInstances [''Diff, ''CommonBody, ''DiffP]
foldDiffsP ::
forall r h.
(Monoid r, Recursively HFoldable h, Recursively HasHPlain h) =>
(forall n. HasHPlain n => HRecWitness h n -> HPlain n -> HPlain n -> r) ->
DiffP # h ->
r
foldDiffsP f =
withDict (recursively (Proxy @(HasHPlain h))) $
\case
CommonSubTreeP{} -> mempty
DifferentP x y -> f HRecSelf x y
CommonBodyP x ->
withDict (recursively (Proxy @(HFoldable h))) $
hfoldMap
( Proxy @(Recursively HFoldable) #*# Proxy @(Recursively HasHPlain) #*#
\w -> foldDiffsP (f . HRecSub w)
) x