polytree-0.1.2: src/Data/PolyTree.hs
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Data.PolyTree (
-- * Types
Tree(..)
, TreeForest(..)
, Tree'
, TreeForest'
, TreeList
, TreeList'
, Tree1
, Tree1'
-- * Construction
, makeTree
, makeChild
, makeLeaves
, makeChildren
, singleton
-- * Traversals
, dfs
, bfs
-- * Utility
, unfoldTree
, unfoldTreeM
, pruneLeaves
, countNodes
, countLeaves
, levels
-- * Optics classes
, GetTree(..)
, HasTree(..)
, GetTreeForest(..)
, HasTreeForest(..)
, ReviewTree(..)
, AsTree(..)
, ReviewTreeForest(..)
, AsTreeForest(..)
-- * Optics
, treeForest'
, treeSubForest
, treeLeaves
, treeForestChildren
-- * Conversions
, baseTree
) where
#if !MIN_VERSION_base(4,18,0)
import Control.Applicative ( Applicative(liftA2) )
#endif
import Control.Lens hiding ((<.>), levels)
import Data.Bifoldable ( Bifoldable(bifoldMap) )
import Data.Bitraversable ( Bitraversable(..) )
import Data.Functor.Apply ( Apply(liftF2, (<.>)) )
import Data.Functor.Classes
( showsBinaryWith,
showsUnaryWith,
Eq1(..),
Eq2(..),
Ord1(..),
Ord2(..),
Show1(liftShowsPrec),
Show2(..) )
import Data.Functor.Identity ()
import Data.List.NonEmpty ( NonEmpty(..), nonEmpty, toList )
import Data.Monoid ( Sum(..) )
import Data.Semigroup.Bifoldable ( Bifoldable1(bifoldMap1) )
import Data.Semigroup.Bitraversable ( Bitraversable1(bitraverse1) )
import Data.Semigroup.Foldable ( Foldable1(foldMap1) )
import Data.Semigroup.Traversable ()
import qualified Data.Tree as Tree
-- $setup
-- >>> import Control.Lens hiding ((<.>), levels)
-- >>> import Data.Functor.Apply ((<.>))
-- >>> import Data.Functor.Identity
-- >>> import Data.List.NonEmpty (NonEmpty(..))
-- >>> import qualified Data.PolyTree
-- >>> :set -XOverloadedStrings
-- | A forest of trees, represented as a functor containing either leaves (b) or subtrees.
-- The 'TreeForest' type allows for a polymorphic container type 'f', enabling different
-- forest representations (lists, vectors, etc.).
--
-- Examples:
--
-- >>> TreeForest [] :: TreeForest [] String String
-- TreeForest []
--
-- >>> TreeForest [Left "leaf1", Left "leaf2"] :: TreeForest [] String String
-- TreeForest [Left "leaf1",Left "leaf2"]
--
-- >>> TreeForest [Right (Tree "node" (TreeForest []))] :: TreeForest [] String String
-- TreeForest [Right (Tree "node" (TreeForest []))]
newtype TreeForest f a b =
TreeForest (f (Either b (Tree f a b)))
type TreeForest' f a =
TreeForest f a a
instance (TreeForest f a b ~ t) =>
Rewrapped (TreeForest f' a' b') t
instance Wrapped (TreeForest f a b) where
type Unwrapped (TreeForest f a b) =
f (Either b (Tree f a b))
_Wrapped' =
iso (\(TreeForest x) -> x) TreeForest
instance Eq1 f => Eq2 (TreeForest f) where
liftEq2 f g (TreeForest x1) (TreeForest x2) =
liftEq (liftEq2 g (liftEq2 f g)) x1 x2
instance Ord1 f => Ord2 (TreeForest f) where
liftCompare2 f g (TreeForest x1) (TreeForest x2) =
liftCompare (liftCompare2 g (liftCompare2 f g)) x1 x2
instance Show1 f => Show2 (TreeForest f) where
liftShowsPrec2 spA slA spB slB d (TreeForest x) =
let spT =
liftShowsPrec2 spA slA spB slB
slT =
liftShowList2 spA slA spB slB
in showsUnaryWith
(liftShowsPrec
(liftShowsPrec2 spB slB spT slT)
(liftShowList2 spB slB spT slT))
"TreeForest"
d
x
instance (Eq a, Eq1 f) => Eq1 (TreeForest f a) where
liftEq =
liftEq2 (==)
instance (Ord a, Ord1 f) => Ord1 (TreeForest f a) where
liftCompare =
liftCompare2 compare
instance (Show a, Show1 f) => Show1 (TreeForest f a) where
liftShowsPrec =
liftShowsPrec2 showsPrec showList
-- |
--
-- >>> TreeForest [Left "a"] == TreeForest [Left "a"] :: Bool
-- True
--
-- >>> TreeForest [Left "a"] == TreeForest [Left "b"] :: Bool
-- False
instance (Eq a, Eq1 f, Eq b) => Eq (TreeForest f a b) where
(==) =
liftEq (==)
-- |
--
-- >>> compare (TreeForest [Left "a"]) (TreeForest [Left "b"]) :: Ordering
-- LT
--
-- >>> compare (TreeForest [Left "z"]) (TreeForest [Left "a"]) :: Ordering
-- GT
instance (Ord a, Ord1 f, Ord b) => Ord (TreeForest f a b) where
compare =
liftCompare compare
-- |
--
-- >>> show (TreeForest [Left "a", makeChild "b" []]) :: String
-- "TreeForest [Left \"a\",Right (Tree \"b\" (TreeForest []))]"
instance (Show a, Show1 f, Show b) => Show (TreeForest f a b) where
showsPrec =
liftShowsPrec showsPrec shows
-- | Map over both the node labels and leaf values in a forest.
--
-- >>> bimap (+1) (*10) (TreeForest [Left 5, makeChild 3 [Left 7]]) :: TreeForest [] Int Int
-- TreeForest [Left 50,Right (Tree 4 (TreeForest [Left 70]))]
instance Functor f => Bifunctor (TreeForest f) where
bimap f g (TreeForest x) =
TreeForest (fmap (bimap g (bimap f g)) x)
-- | Map over the leaf values in a forest, keeping node labels unchanged.
--
-- >>> fmap (*2) (TreeForest [Left 5, Left 10]) :: TreeForest [] Int Int
-- TreeForest [Left 10,Left 20]
instance Functor f => Functor (TreeForest f a) where
fmap =
bimap id
-- NOTE: Biapply and Biapplicative instances are NOT possible for TreeForest
-- because the recursive structure makes it impossible to correctly handle the
-- case where we have a tree of functions and a single value (or vice versa).
-- The bifunctor structure over (a, b) doesn't align with the Either-based
-- recursive forest representation.
-- | Apply instance for TreeForest using semigroup on node labels.
--
-- >>> TreeForest [Left (+1), Left (*2)] <.> TreeForest [Left 5] :: TreeForest [] String Int
-- TreeForest [Left 6,Left 10]
instance (Apply f, Semigroup a) => Apply (TreeForest f a) where
TreeForest x1 <.> TreeForest x2 =
let combine (Left f) (Left x) =
Left (f x)
combine (Left f) (Right tx) =
Right (fmap f tx)
combine (Right tf) (Left x) =
Right (fmap ($ x) tf)
combine (Right tf) (Right tx) =
Right (tf <.> tx)
in TreeForest (liftF2 combine x1 x2)
-- | Applicative instance for TreeForest using monoid on node labels.
--
-- >>> pure 42 :: TreeForest [] String Int
-- TreeForest [Left 42]
--
-- >>> TreeForest [Left (+1)] <*> TreeForest [Left 5, Left 10] :: TreeForest [] String Int
-- TreeForest [Left 6,Left 11]
instance (Applicative f, Monoid a) => Applicative (TreeForest f a) where
pure b =
TreeForest (pure (Left b))
TreeForest x1 <*> TreeForest x2 =
let combine (Left f) (Left x) =
Left (f x)
combine (Left f) (Right tx) =
Right (fmap f tx)
combine (Right tf) (Left x) =
Right (fmap ($ x) tf)
combine (Right tf) (Right tx) =
Right (tf <*> tx)
in TreeForest (liftA2 combine x1 x2)
-- | Fold over both node labels and leaf values in a forest.
--
-- >>> import Data.Monoid (Sum(..))
-- >>> bifoldMap Sum Sum (TreeForest [Left 5, makeChild 3 [Left 7]]) :: Sum Int
-- Sum {getSum = 15}
instance Foldable f => Bifoldable (TreeForest f) where
bifoldMap f g (TreeForest x) =
foldMap (either g (bifoldMap f g)) x
-- | Bifoldable1 for non-empty forests.
--
-- >>> import Data.Semigroup (Sum(..))
-- >>> bifoldMap1 Sum Sum (TreeForest (Left 5 :| [Left 3])) :: Sum Int
-- Sum {getSum = 8}
instance Foldable1 f => Bifoldable1 (TreeForest f) where
bifoldMap1 f g (TreeForest x) =
foldMap1 (either g (bifoldMap1 f g)) x
-- | Fold over the leaf values in a forest.
--
-- >>> foldMap (:[]) (TreeForest [Left "a", Left "b", makeChild 1 [Left "c"]]) :: [String]
-- ["a","b","c"]
instance Foldable f => Foldable (TreeForest f a) where
foldMap f (TreeForest x) =
foldMap (either f (foldMap f)) x
-- | Foldable1 for non-empty forests.
--
-- >>> import Data.Semigroup (Sum(..))
-- >>> foldMap1 Sum (TreeForest (Left 5 :| [Left 3])) :: Sum Int
-- Sum {getSum = 8}
instance Foldable1 f => Foldable1 (TreeForest f a) where
foldMap1 f (TreeForest x) =
foldMap1 (either f (foldMap1 f)) x
-- | Traverse both node labels and leaf values with effects.
--
-- >>> bitraverse Just Just (TreeForest [Left "x"]) :: Maybe (TreeForest [] String String)
-- Just (TreeForest [Left "x"])
instance Traversable f => Bitraversable (TreeForest f) where
bitraverse f g (TreeForest x) =
TreeForest <$> traverse (either (fmap Left . g) (fmap Right . bitraverse f g)) x
-- | Bitraversable1 for non-empty forests.
--
-- >>> import Data.Functor.Identity
-- >>> bitraverse1 Identity Identity (TreeForest (Left "x" :| [])) :: Identity (TreeForest NonEmpty String String)
-- Identity (TreeForest (Left "x" :| []))
instance Traversable1 f => Bitraversable1 (TreeForest f) where
bitraverse1 f g (TreeForest x) =
TreeForest <$> traverse1 (either (fmap Left . g) (fmap Right . bitraverse1 f g)) x
-- | Traverse the leaf values with effects.
--
-- >>> traverse Just (TreeForest [Left "x", Left "y"]) :: Maybe (TreeForest [] Int String)
-- Just (TreeForest [Left "x",Left "y"])
instance Traversable f => Traversable (TreeForest f a) where
traverse f (TreeForest x) =
TreeForest <$> traverse (either (fmap Left . f) (fmap Right . traverse f)) x
-- | Traversable1 for non-empty forests.
--
-- >>> import Data.Functor.Identity
-- >>> traverse1 Identity (TreeForest (Left "x" :| [])) :: Identity (TreeForest NonEmpty Int String)
-- Identity (TreeForest (Left "x" :| []))
instance Traversable1 f => Traversable1 (TreeForest f a) where
traverse1 f (TreeForest x) =
TreeForest <$> traverse1 (either (fmap Left . f) (fmap Right . traverse1 f)) x
-- | Read-only access to a tree forest structure via a 'Getter'.
class GetTreeForest x f a b | x -> f a b where
-- | Extract the tree forest from a structure.
getTreeForest ::
Getter x (TreeForest f a b)
-- |
--
-- >>> view getTreeForest (TreeForest [Left "a", makeChild "b" []]) == TreeForest [Left "a", Right (Tree "b" (TreeForest []))]
-- True
instance GetTreeForest (TreeForest f a b) f a b where
getTreeForest =
to id
-- | Read-write access to a tree forest structure via a 'Lens'.
-- This extends 'GetTreeForest' to allow modification.
class GetTreeForest x f a b => HasTreeForest x f a b | x -> f a b where
-- | Access the tree forest with read-write capability.
treeForest ::
Lens' x (TreeForest f a b)
-- |
--
-- >>> view treeForest (TreeForest []) :: TreeForest [] String String
-- TreeForest []
instance HasTreeForest (TreeForest f a b) f a b where
treeForest =
id
-- | Construction-only access to a tree forest via a 'Review'.
class ReviewTreeForest x f a b | x -> f a b where
-- | Construct a value from a tree forest.
reviewTreeForest ::
Review x (TreeForest f a b)
-- |
--
-- >>> review reviewTreeForest (TreeForest [Left "x"]) :: TreeForest [] String String
-- TreeForest [Left "x"]
instance ReviewTreeForest (TreeForest f a b) f a b where
reviewTreeForest =
unto id
-- | Full prism access to a tree forest structure.
-- This extends 'ReviewTreeForest' to allow both construction and pattern matching.
class ReviewTreeForest x f a b => AsTreeForest x f a b | x -> f a b where
-- | Access the tree forest as a prism (construct or pattern match).
_TreeForest ::
Prism' x (TreeForest f a b)
-- |
--
-- >>> preview _TreeForest (TreeForest [Left "y"]) :: Maybe (TreeForest [] String String)
-- Just (TreeForest [Left "y"])
instance AsTreeForest (TreeForest f a b) f a b where
_TreeForest =
id
-- | A polymorphic rose tree with different types for node labels (a) and leaf values (b).
-- The container type 'f' allows for different representations of child forests.
--
-- Examples:
--
-- >>> Tree "root" (TreeForest []) :: TreeList String String
-- Tree "root" (TreeForest [])
--
-- >>> Tree "root" (TreeForest [Left "leaf1", Left "leaf2"]) :: TreeList String String
-- Tree "root" (TreeForest [Left "leaf1",Left "leaf2"])
--
-- >>> Tree "root" (TreeForest [makeChild "child" [Left "leaf"]]) :: TreeList String String
-- Tree "root" (TreeForest [Right (Tree "child" (TreeForest [Left "leaf"]))])
data Tree f a b =
Tree a (TreeForest f a b)
-- | A tree where node labels and leaf values have the same type.
type Tree' f a =
Tree f a a
-- | A tree using lists for the forest container.
type TreeList a b =
Tree [] a b
-- | A list-based tree where nodes and leaves have the same type.
type TreeList' a =
TreeList a a
-- | A tree with a single child (using Identity).
type Tree1 a b =
Tree Identity a b
-- | A single-child tree where nodes and leaves have the same type.
type Tree1' a =
Tree1 a a
instance Eq1 f => Eq2 (Tree f) where
liftEq2 f g (Tree a t1) (Tree b t2) =
f a b &&
liftEq2 f g t1 t2
instance Ord1 f => Ord2 (Tree f) where
liftCompare2 f g (Tree a t1) (Tree b t2) =
f a b <>
liftCompare2 f g t1 t2
instance Show1 f => Show2 (Tree f) where
liftShowsPrec2 spA slA spB slB d (Tree a t) =
showsBinaryWith
spA
(liftShowsPrec2 spA slA spB slB)
"Tree"
d
a
t
instance (Eq a, Eq1 f) => Eq1 (Tree f a) where
liftEq =
liftEq2 (==)
instance (Ord a, Ord1 f) => Ord1 (Tree f a) where
liftCompare =
liftCompare2 compare
instance (Show a, Show1 f) => Show1 (Tree f a) where
liftShowsPrec =
liftShowsPrec2 showsPrec showList
-- |
--
-- >>> Tree "a" (TreeForest []) == Tree "a" (TreeForest []) :: Bool
-- True
--
-- >>> Tree "a" (TreeForest [Left 1]) == Tree "a" (TreeForest [Left 2]) :: Bool
-- False
instance (Eq a, Eq1 f, Eq b) => Eq (Tree f a b) where
(==) =
liftEq (==)
-- |
--
-- >>> compare (Tree "a" (TreeForest [])) (Tree "b" (TreeForest [])) :: Ordering
-- LT
instance (Ord a, Ord1 f, Ord b) => Ord (Tree f a b) where
compare =
liftCompare compare
-- |
--
-- >>> show (Tree "root" (TreeForest [Left "x"])) :: String
-- "Tree \"root\" (TreeForest [Left \"x\"])"
instance (Show a, Show1 f, Show b) => Show (Tree f a b) where
showsPrec =
liftShowsPrec showsPrec shows
-- | Map over both node labels and leaf values.
--
-- >>> bimap (+1) (*10) (Tree 5 (TreeForest [Left 3])) :: TreeList Int Int
-- Tree 6 (TreeForest [Left 30])
instance Functor f => Bifunctor (Tree f) where
bimap f g (Tree a t) =
Tree (f a) (bimap f g t)
-- | Map over leaf values, keeping node labels unchanged.
--
-- >>> fmap (*2) (Tree "root" (TreeForest [Left 5, Left 10])) :: TreeList String Int
-- Tree "root" (TreeForest [Left 10,Left 20])
instance Functor f => Functor (Tree f a) where
fmap =
bimap id
-- NOTE: Biapply and Biapplicative instances are NOT possible for Tree
-- because the underlying TreeForest cannot support these instances due to
-- the recursive Either-based structure. See the note on TreeForest above.
-- | Apply with semigroup combination of node labels.
--
-- >>> Tree "a" (TreeForest [Left (+1)]) <.> Tree "b" (TreeForest [Left 5]) :: TreeList String Int
-- Tree "ab" (TreeForest [Left 6])
instance (Apply f, Semigroup a) => Apply (Tree f a) where
Tree a1 t1 <.> Tree a2 t2 =
Tree (a1 <> a2) (t1 <.> t2)
-- |
--
-- >>> Tree "a" (TreeForest []) <*> Tree "b" (TreeForest []) :: TreeList String String
-- Tree "ab" (TreeForest [])
--
-- >>> Tree "a" (TreeForest [Left Prelude.reverse]) <*> Tree "b" (TreeForest [Left "xyz"]) :: TreeList String String
-- Tree "ab" (TreeForest [Left "zyx"])
--
-- >>> Tree "a" (TreeForest [Left Prelude.reverse]) <*> Tree "b" (TreeForest [Left "xyz", makeChild "c" [Left "pqr"], makeChild "d" [Left "mno"]]) :: TreeList String String
-- Tree "ab" (TreeForest [Left "zyx",Right (Tree "c" (TreeForest [Left "rqp"])),Right (Tree "d" (TreeForest [Left "onm"]))])
instance (Applicative f, Monoid a) => Applicative (Tree f a) where
pure =
Tree mempty . pure
Tree a1 t1 <*> Tree a2 t2 =
Tree (a1 <> a2) (t1 <*> t2)
-- | Fold over both node labels and leaf values.
--
-- >>> import Data.Monoid (Sum(..))
-- >>> bifoldMap Sum Sum (Tree 1 (TreeForest [Left 5, makeChild 3 []])) :: Sum Int
-- Sum {getSum = 9}
instance Foldable f => Bifoldable (Tree f) where
bifoldMap f g (Tree a t) =
f a <> bifoldMap f g t
-- | Bifoldable1 for trees with non-empty forests.
--
-- >>> import Data.Semigroup (Sum(..))
-- >>> bifoldMap1 Sum Sum (Tree 1 (TreeForest (Left 5 :| []))) :: Sum Int
-- Sum {getSum = 6}
instance Foldable1 f => Bifoldable1 (Tree f) where
bifoldMap1 f g (Tree a t) =
f a <> bifoldMap1 f g t
-- | Fold over leaf values only (ignores node labels).
--
-- >>> foldMap (:[]) (Tree "root" (TreeForest [Left "a", Left "b"])) :: [String]
-- ["a","b"]
instance Foldable f => Foldable (Tree f a) where
foldMap f (Tree _ t) =
foldMap f t
-- | Foldable1 for trees with non-empty forests.
--
-- >>> import Data.Semigroup (Sum(..))
-- >>> foldMap1 Sum (Tree "root" (TreeForest (Left 5 :| [Left 10]))) :: Sum Int
-- Sum {getSum = 15}
instance Foldable1 f => Foldable1 (Tree f a) where
foldMap1 f (Tree _ t) =
foldMap1 f t
-- | Traverse both node labels and leaf values with effects.
--
-- >>> bitraverse Just Just (Tree "a" (TreeForest [Left "b"])) :: Maybe (TreeList String String)
-- Just (Tree "a" (TreeForest [Left "b"]))
instance Traversable f => Bitraversable (Tree f) where
bitraverse f g (Tree a t) =
Tree <$> f a <*> bitraverse f g t
-- | Bitraversable1 for trees with non-empty forests.
--
-- >>> import Data.Functor.Identity
-- >>> bitraverse1 Identity Identity (Tree "a" (TreeForest (Left "b" :| []))) :: Identity (Tree NonEmpty String String)
-- Identity (Tree "a" (TreeForest (Left "b" :| [])))
instance Traversable1 f => Bitraversable1 (Tree f) where
bitraverse1 f g (Tree a t) =
Tree <$> f a <.> bitraverse1 f g t
-- | Traverse leaf values with effects.
--
-- >>> traverse Just (Tree "root" (TreeForest [Left "x"])) :: Maybe (TreeList String String)
-- Just (Tree "root" (TreeForest [Left "x"]))
instance Traversable f => Traversable (Tree f a) where
traverse f (Tree a t) =
Tree a <$> traverse f t
-- | Traversable1 for trees with non-empty forests.
--
-- >>> import Data.Functor.Identity
-- >>> traverse1 Identity (Tree "root" (TreeForest (Left "x" :| []))) :: Identity (Tree NonEmpty String String)
-- Identity (Tree "root" (TreeForest (Left "x" :| [])))
instance Traversable1 f => Traversable1 (Tree f a) where
traverse1 f (Tree a t) =
Tree a <$> traverse1 f t
-- | Plated instance for recursive tree traversal.
--
-- >>> transform (\(Tree a f) -> Tree (a <> "!") f) (Tree "a" (TreeForest [makeChild "b" []])) :: TreeList String String
-- Tree "a!" (TreeForest [Right (Tree "b!" (TreeForest []))])
instance Traversable f => Plated (Tree f a b) where
plate f (Tree a (TreeForest x)) =
Tree a . TreeForest <$> traverse
( either
(pure . Left)
(fmap Right . f)
) x
-- | Lens into a tree's forest.
--
-- >>> view treeForest' (Tree "a" (TreeForest [Left "x"])) :: TreeForest [] String String
-- TreeForest [Left "x"]
--
-- >>> set treeForest' (TreeForest [Left "y"]) (Tree "a" (TreeForest [Left "x"])) :: TreeList String String
-- Tree "a" (TreeForest [Left "y"])
treeForest' ::
Lens
(Tree f a b)
(Tree f' a b')
(TreeForest f a b)
(TreeForest f' a b')
treeForest' f (Tree a t) =
fmap (Tree a) (f t)
-- | Traversal over the immediate children and leaves of a tree.
--
-- >>> toListOf treeSubForest (Tree "root" (TreeForest [Left "x", makeChild "c" []])) :: [Either String (TreeList String String)]
-- [Left "x",Right (Tree "c" (TreeForest []))]
treeSubForest ::
Traversable f =>
Traversal
(Tree f a b)
(Tree f a b')
(Either b (Tree f a b))
(Either b' (Tree f a b'))
treeSubForest =
treeForest' . _Wrapped . traverse
-- | Traversal over all immediate leaves in a tree (not recursive).
--
-- >>> toListOf treeLeaves (Tree "root" (TreeForest [Left "x", Left "y"])) :: [String]
-- ["x","y"]
--
-- >>> over treeLeaves (*2) (Tree "root" (TreeForest [Left 5, Left 10])) :: TreeList String Int
-- Tree "root" (TreeForest [Left 10,Left 20])
treeLeaves ::
Traversable f =>
Traversal'
(Tree f a b)
b
treeLeaves =
treeSubForest . _Left
-- | Traversal over the immediate child trees.
--
-- >>> lengthOf treeForestChildren (Tree "root" (TreeForest [Left "x", makeChild "c1" [], makeChild "c2" []]))
-- 2
treeForestChildren ::
Traversable f =>
Traversal'
(Tree f a b)
(Tree f a b)
treeForestChildren =
treeSubForest . _Right
-- | Read-only access to a tree structure via a 'Getter'.
-- This is the most general class, allowing types to expose a tree view
-- without necessarily allowing modification.
class GetTree x f a b | x -> f a b where
-- | Extract the tree from a structure.
getTree ::
Getter x (Tree f a b)
{-# INLINE getTreeLabel #-}
-- | Extract just the tree's label.
getTreeLabel ::
Getter x a
getTreeLabel =
getTree . getTreeLabel
-- |
--
-- >>> view getTreeLabel (Tree "a" (TreeForest []))
-- "a"
--
-- >>> view getTree (Tree "a" (TreeForest [])) == Tree "a" (TreeForest [])
-- True
instance GetTree (Tree f a b) f a b where
getTree =
to id
{-# INLINE getTreeLabel #-}
getTreeLabel =
to (\(Tree a _) -> a)
-- | Read-write access to a tree structure via a 'Lens'.
-- This extends 'GetTree' to allow modification.
class GetTree x f a b => HasTree x f a b | x -> f a b where
-- | Access the tree with read-write capability.
tree ::
Lens' x (Tree f a b)
{-# INLINE treeLabel #-}
-- | Access the tree's label with read-write capability.
treeLabel ::
Lens' x a
treeLabel =
tree . treeLabel
-- |
--
-- >>> view treeLabel (Tree "a" (TreeForest []))
-- "a"
--
-- >>> set treeLabel "z" (Tree "a" (TreeForest [])) :: TreeList String String
-- Tree "z" (TreeForest [])
--
-- >>> view treeForest (Tree "a" (TreeForest []))
-- TreeForest []
--
-- >>> view treeForest (Tree "b" (TreeForest [Left "xyz", makeChild "c" [Left "pqr"], makeChild "d" [Left "mno"]]))
-- TreeForest [Left "xyz",Right (Tree "c" (TreeForest [Left "pqr"])),Right (Tree "d" (TreeForest [Left "mno"]))]
instance HasTree (Tree f a b) f a b where
tree =
id
{-# INLINE treeLabel #-}
treeLabel f (Tree a t) =
fmap (`Tree` t) (f a)
-- |
--
-- >>> view getTreeForest (Tree "root" (TreeForest [Left "a"])) :: TreeForest [] String String
-- TreeForest [Left "a"]
instance GetTreeForest (Tree f a b) f a b where
getTreeForest =
to (\(Tree _ forest) -> forest)
-- |
--
-- >>> view treeForest (Tree "root" (TreeForest [Left "a"])) :: TreeForest [] String String
-- TreeForest [Left "a"]
--
-- >>> set treeForest (TreeForest [Left "b"]) (Tree "root" (TreeForest [Left "a"])) :: TreeList String String
-- Tree "root" (TreeForest [Left "b"])
instance HasTreeForest (Tree f a b) f a b where
treeForest =
treeForest'
-- | Construction-only access to a tree via a 'Review'.
-- This allows building a type from a tree.
class ReviewTree x f a b | x -> f a b where
-- | Construct a value from a tree.
reviewTree ::
Review x (Tree f a b)
-- |
--
-- >>> review reviewTree (Tree "hello" (TreeForest [])) :: TreeList String String
-- Tree "hello" (TreeForest [])
instance ReviewTree (Tree f a b) f a b where
reviewTree =
unto id
-- | Full prism access to a tree structure.
-- This extends 'ReviewTree' to allow both construction and pattern matching.
class ReviewTree x f a b => AsTree x f a b | x -> f a b where
-- | Access the tree as a prism (construct or pattern match).
_Tree ::
Prism' x (Tree f a b)
-- |
--
-- >>> preview _Tree (Tree "test" (TreeForest [])) :: Maybe (TreeList String String)
-- Just (Tree "test" (TreeForest []))
instance AsTree (Tree f a b) f a b where
_Tree =
id
-- |
--
-- >>> dfs (Tree 1 (TreeForest []))
-- Left 1 :| []
--
-- >>> dfs (Tree 1 (TreeForest [Left 2]))
-- Left 1 :| [Right 2]
--
-- >>> dfs (Tree 1 (TreeForest [Left 2, makeChild 3 []]))
-- Left 1 :| [Right 2,Left 3]
--
-- >>> dfs (Tree 1 (TreeForest [Left 2, makeChild 3 [], Left 4]))
-- Left 1 :| [Right 2,Left 3,Right 4]
--
-- >>> dfs (Tree 1 (TreeForest [Left 2, makeChild 3 [Left 5], Left 4]))
-- Left 1 :| [Right 2,Left 3,Right 5,Right 4]
--
-- >>> dfs (Tree 1 (TreeForest [Left 2, makeChild 3 [Left 5], Left 4, makeChild 6 []]))
-- Left 1 :| [Right 2,Left 3,Right 5,Right 4,Left 6]
dfs ::
Foldable f =>
Tree f a b ->
NonEmpty (Either a b)
dfs (Tree a t) =
Left a :| foldMap (either (\b -> [Right b]) (toList . dfs)) (view _Wrapped t)
-- |
--
-- >>> bfs (Tree 1 (TreeForest []))
-- Left 1 :| []
--
-- >>> bfs (Tree 1 (TreeForest [Left 2]))
-- Left 1 :| [Right 2]
--
-- >>> bfs (Tree 1 (TreeForest [Left 2, makeChild 3 []]))
-- Left 1 :| [Right 2,Left 3]
--
-- >>> bfs (Tree 1 (TreeForest [Left 2, makeChild 3 [], Left 4]))
-- Left 1 :| [Right 2,Right 4,Left 3]
--
-- >>> bfs (Tree 1 (TreeForest [Left 2, makeChild 3 [Left 5], Left 4]))
-- Left 1 :| [Right 2,Right 4,Left 3,Right 5]
--
-- >>> bfs (Tree 1 (TreeForest [Left 2, makeChild 3 [Left 5], Left 4, makeChild 6 []]))
-- Left 1 :| [Right 2,Right 4,Left 3,Right 5,Left 6]
bfs ::
Foldable f =>
Tree f a b
-> NonEmpty (Either a b)
bfs root =
let go (Tree a t :| rest) =
let (leaves, c) =
foldMap (either (\b -> ([Right b], [])) (\tr -> ([], [tr]))) (view _Wrapped t)
in case nonEmpty (rest <> c) of
Nothing -> Left a :| leaves
Just q -> Left a :| (leaves <> toList (go q))
in go (root :| [])
-- |
--
-- >>> makeTree 1 []
-- Tree 1 (TreeForest [])
--
-- >>> makeTree 1 [Left "a"]
-- Tree 1 (TreeForest [Left "a"])
--
-- >>> makeTree 1 [Left "a", Right (makeTree 2 [])]
-- Tree 1 (TreeForest [Left "a",Right (Tree 2 (TreeForest []))])
makeTree ::
a
-> f (Either b (Tree f a b))
-> Tree f a b
makeTree a t =
Tree a (TreeForest t)
-- |
--
-- >>> makeChild 1 []
-- Right (Tree 1 (TreeForest []))
--
-- >>> makeChild 1 [Left "a"]
-- Right (Tree 1 (TreeForest [Left "a"]))
--
-- >>> makeChild 1 [Left "a", makeChild 2 []]
-- Right (Tree 1 (TreeForest [Left "a",Right (Tree 2 (TreeForest []))]))
makeChild ::
a
-> f (Either b (Tree f a b))
-> Either x (Tree f a b)
makeChild a t =
Right (makeTree a t)
-- |
--
-- >>> makeLeaves 1 []
-- Tree 1 (TreeForest [])
--
-- >>> makeLeaves 1 [makeLeaves 2 []]
-- Tree 1 (TreeForest [Left (Tree 2 (TreeForest []))])
--
-- >>> makeLeaves 1 [makeLeaves 2 [makeChild 3 []]]
-- Tree 1 (TreeForest [Left (Tree 2 (TreeForest [Left (Right (Tree 3 (TreeForest [])))]))])
makeLeaves ::
Functor f =>
a
-> f b
-> Tree f a b
makeLeaves a bs =
Tree a (TreeForest (Left <$> bs))
-- |
--
-- >>> makeChildren 1 []
-- Tree 1 (TreeForest [])
--
-- >>> makeChildren 1 [makeChildren 2 []]
-- Tree 1 (TreeForest [Right (Tree 2 (TreeForest []))])
--
-- >>> makeChildren 1 [makeChildren 2 [], makeLeaves 3 []]
-- Tree 1 (TreeForest [Right (Tree 2 (TreeForest [])),Right (Tree 3 (TreeForest []))])
makeChildren ::
Functor f =>
a
-> f (Tree f a b)
-> Tree f a b
makeChildren a cs =
Tree a (TreeForest (Right <$> cs))
-- |
--
-- >>> singleton "root" :: TreeList String String
-- Tree "root" (TreeForest [])
singleton ::
Monoid (f (Either b (Tree f a b))) =>
a
-> Tree f a b
singleton a =
Tree a (TreeForest mempty)
-- |
--
-- >>> unfoldTree (\n -> (n, if n < 3 then [Left (n * 10), Right (n + 1)] else [])) 1 :: TreeList Int Int
-- Tree 1 (TreeForest [Left 10,Right (Tree 2 (TreeForest [Left 20,Right (Tree 3 (TreeForest []))]))])
unfoldTree ::
Functor f =>
(a -> (a, f (Either b a)))
-> a
-> Tree f a b
unfoldTree f seed =
let (label, forest) = f seed
in Tree label (TreeForest (fmap (fmap (unfoldTree f)) forest))
-- |
--
-- >>> unfoldTreeM (\n -> pure (n, if n < 3 then [Left (n * 10), Right (n + 1)] else [])) 1 :: Maybe (TreeList Int Int)
-- Just (Tree 1 (TreeForest [Left 10,Right (Tree 2 (TreeForest [Left 20,Right (Tree 3 (TreeForest []))]))]))
unfoldTreeM ::
(Monad m, Traversable f) =>
(a -> m (a, f (Either b a)))
-> a
-> m (Tree f a b)
unfoldTreeM f seed = do
(label, forest) <- f seed
forest' <- traverse (either (pure . Left) (fmap Right . unfoldTreeM f)) forest
pure (Tree label (TreeForest forest'))
-- |
--
-- >>> pruneLeaves (> 2) (Tree 1 (TreeForest [Left 1, Left 3, makeChild 2 [Left 2, Left 4]])) :: TreeList Int Int
-- Tree 1 (TreeForest [Left 3,Right (Tree 2 (TreeForest [Left 4]))])
pruneLeaves ::
(Traversable f, Applicative f, Monoid (f (Either b (Tree f a b)))) =>
(b -> Bool)
-> Tree f a b
-> Tree f a b
pruneLeaves p (Tree a (TreeForest forest)) =
Tree a (TreeForest (foldMap go forest))
where
go (Left b) = if p b then pure (Left b) else mempty
go (Right t) = pure (Right (pruneLeaves p t))
-- |
--
-- >>> countNodes (Tree 1 (TreeForest [Left 2, makeChild 3 [Left 4], makeChild 5 []])) :: Int
-- 3
countNodes ::
Foldable f =>
Tree f a b
-> Int
countNodes (Tree _ (TreeForest forest)) =
1 + getSum (foldMap (either (const (Sum 0)) (Sum . countNodes)) forest)
-- |
--
-- >>> countLeaves (Tree 1 (TreeForest [Left 2, makeChild 3 [Left 4], makeChild 5 []])) :: Int
-- 2
countLeaves ::
Foldable f =>
Tree f a b
-> Int
countLeaves (Tree _ (TreeForest forest)) =
getSum (foldMap (either (const (Sum 1)) (Sum . countLeaves)) forest)
-- | Group tree elements by their depth level.
--
-- >>> Data.PolyTree.levels (Tree 1 (TreeForest [Left 2, makeChild 3 [], Left 7])) :: [[Either Int Int]]
-- [[Left 1],[Right 2],[Right 7],[Left 3]]
levels ::
Foldable f =>
Tree f a b
-> [[Either a b]]
levels t =
case bfs t of
root :| rest -> groupByLevel [root] rest
where
groupByLevel acc [] = [acc]
groupByLevel acc xs =
let (level, remaining) = splitAt (length acc) xs
in if null level
then [acc]
else acc : groupByLevel level remaining
-- |
--
-- >>> view baseTree (Tree 1 (TreeForest []))
-- Node {rootLabel = 1, subForest = []}
--
-- >>> view baseTree (Tree 1 (TreeForest [Left 2, makeChild 3 [Left 5], Left 4, makeChild 6 []]))
-- Node {rootLabel = 1, subForest = [Node {rootLabel = 2, subForest = []},Node {rootLabel = 3, subForest = [Node {rootLabel = 5, subForest = []}]},Node {rootLabel = 4, subForest = []},Node {rootLabel = 6, subForest = []}]}
--
-- >>> review baseTree (Tree.Node 1 [])
-- Tree 1 (TreeForest [])
--
-- >>> review baseTree (Tree.Node 1 [Tree.Node 2 [],Tree.Node 3 [Tree.Node 5 []],Tree.Node 4 [],Tree.Node 6 []])
-- Tree 1 (TreeForest [Left 2,Right (Tree 3 (TreeForest [Left 5])),Left 4,Left 6])
baseTree ::
Iso' (TreeList' a) (Tree.Tree a)
baseTree =
iso
(
let go (Tree a t) =
Tree.Node a (fmap (either pure go) (view _Wrapped t))
in go)
(
let perNode (Tree.Node a []) =
Left a
perNode tr@(Tree.Node _ (_:_)) =
Right tr
go (Tree.Node a t) =
Tree a (TreeForest (fmap (fmap go . perNode) t))
in go)