packages feed

dag 0.0.2.1 → 0.1

raw patch · 4 files changed

+162/−78 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.Graph.DAG: GCons :: String -> a -> (DAG es a) -> DAG es a
- Data.Graph.DAG: GNil :: (EdgeSchema es x unique) -> DAG es a
- Data.Graph.DAG: instance Functor (DAG es)
- Data.Graph.DAG.Edge.Utils: Node :: a_aa2i -> [Tree a_aa2i] -> Tree a_aa2i
- Data.Graph.DAG.Edge.Utils: NodeSym0KindInference :: NodeSym0
- Data.Graph.DAG.Edge.Utils: NodeSym1KindInference :: NodeSym1
- Data.Graph.DAG.Edge.Utils: data NodeSym0 (l_aa2w :: TyFun a_aa2i (TyFun ([] (Tree a_aa2i)) (Tree a_aa2i) -> *))
- Data.Graph.DAG.Edge.Utils: data NodeSym1 (l_aa2z :: a_aa2i) (l_aa2y :: TyFun ([] (Tree a_aa2i)) (Tree a_aa2i))
- Data.Graph.DAG.Edge.Utils: data Tree a_aa2i
- Data.Graph.DAG.Edge.Utils: instance (SingI n0, SingI n1) => SingI ('Node n0 n1)
- Data.Graph.DAG.Edge.Utils: instance Applicative Tree
- Data.Graph.DAG.Edge.Utils: instance Eq a0 => Eq (Tree a0)
- Data.Graph.DAG.Edge.Utils: instance Functor Tree
- Data.Graph.DAG.Edge.Utils: instance Monad Tree
- Data.Graph.DAG.Edge.Utils: instance Monoid a => Monoid (Tree a)
- Data.Graph.DAG.Edge.Utils: instance Show a0 => Show (Tree a0)
- Data.Graph.DAG.Edge.Utils: instance SuppressUnusedWarnings NodeSym0
- Data.Graph.DAG.Edge.Utils: instance SuppressUnusedWarnings NodeSym1
- Data.Graph.DAG.Edge.Utils: type NodeSym2 (t_aa2u :: a_aa2i) (t_aa2v :: [] (Tree a_aa2i)) = Node t_aa2u t_aa2v
- Data.Graph.DAG.Edge.Utils: type STree (z_aa2B :: Tree a_aa2i) = Sing z_aa2B
+ Data.Graph.DAG: DAG :: (EdgeSchema es x u) -> (NodeSchema a) -> DAG es x u a
+ Data.Graph.DAG: getEdgeSchema :: DAG es x u a -> (EdgeSchema es x u)
+ Data.Graph.DAG: getNodeSchema :: DAG es x u a -> (NodeSchema a)
+ Data.Graph.DAG: instance Functor (DAG es x u)
+ Data.Graph.DAG.Edge.Utils: (:@->$###) :: (:@->$)
+ Data.Graph.DAG.Edge.Utils: (:@->$$###) :: (:@->$$)
+ Data.Graph.DAG.Edge.Utils: (:@->) :: a_abad -> [RTree a_abad] -> RTree a_abad
+ Data.Graph.DAG.Edge.Utils: data (:@->$) (l_abar :: TyFun a_abad (TyFun ([] (RTree a_abad)) (RTree a_abad) -> *))
+ Data.Graph.DAG.Edge.Utils: data RTree a_abad
+ Data.Graph.DAG.Edge.Utils: eForestToEdges :: [RTree String] -> [(String, String)]
+ Data.Graph.DAG.Edge.Utils: eTreeToEdges :: RTree String -> [(String, String)]
+ Data.Graph.DAG.Edge.Utils: ehead :: (EdgeType from to ~ b, EdgeValue from to ~ a) => EdgeSchema (b : old) c u -> a
+ Data.Graph.DAG.Edge.Utils: espanningtrees :: SingI (SpanningTrees' es []) => EdgeSchema es x unique -> Demote (SpanningTrees' es [])
+ Data.Graph.DAG.Edge.Utils: etree :: SingI (SpanningTrees' es []) => String -> EdgeSchema es x unique -> Maybe (RTree String)
+ Data.Graph.DAG.Edge.Utils: fcEdges :: SingI (SpanningTrees' es []) => EdgeSchema es x True -> [(String, String)]
+ Data.Graph.DAG.Edge.Utils: instance (SingI n0, SingI n1) => SingI (n0 ':@-> n1)
+ Data.Graph.DAG.Edge.Utils: instance Applicative RTree
+ Data.Graph.DAG.Edge.Utils: instance Eq a0 => Eq (RTree a0)
+ Data.Graph.DAG.Edge.Utils: instance Foldable RTree
+ Data.Graph.DAG.Edge.Utils: instance Functor RTree
+ Data.Graph.DAG.Edge.Utils: instance Monad RTree
+ Data.Graph.DAG.Edge.Utils: instance Monoid a => Monoid (RTree a)
+ Data.Graph.DAG.Edge.Utils: instance Show a0 => Show (RTree a0)
+ Data.Graph.DAG.Edge.Utils: instance SuppressUnusedWarnings (:@->$$)
+ Data.Graph.DAG.Edge.Utils: instance SuppressUnusedWarnings (:@->$)
+ Data.Graph.DAG.Edge.Utils: type (:@->$$$) (t_abap :: a_abad) (t_abaq :: [] (RTree a_abad)) = (:@->) t_abap t_abaq
+ Data.Graph.DAG.Edge.Utils: type SRTree (z_abaw :: RTree a_abad) = Sing z_abaw
+ Data.Graph.DAG.Node: data NodeSchema a
+ Data.Graph.DAG.Node: instance Eq a => Eq (NodeSchema a)
+ Data.Graph.DAG.Node: instance Functor NodeSchema
+ Data.Graph.DAG.Node: instance Monoid (NodeSchema a)
+ Data.Graph.DAG.Node: instance Show a => Show (NodeSchema a)
+ Data.Graph.DAG.Node: nadd :: String -> a -> NodeSchema a -> NodeSchema a
+ Data.Graph.DAG.Node: ncombine :: NodeSchema a -> NodeSchema a -> NodeSchema a
+ Data.Graph.DAG.Node: nempty :: NodeSchema a
+ Data.Graph.DAG.Node: nlookup :: String -> NodeSchema a -> Maybe a
+ Data.Graph.DAG.Node: nremove :: String -> NodeSchema a -> NodeSchema a
- Data.Graph.DAG: data DAG es a
+ Data.Graph.DAG: data DAG es x u a
- Data.Graph.DAG: glookup :: String -> DAG es a -> Maybe a
+ Data.Graph.DAG: glookup :: String -> DAG es x u a -> Maybe a

Files

dag.cabal view
@@ -1,10 +1,10 @@ Name:                   dag-Version:                0.0.2.1+Version:                0.1 Author:                 Athan Clark <athan.clark@gmail.com> Maintainer:             Athan Clark <athan.clark@gmail.com> License:                BSD3 License-File:           LICENSE-Synopsis:               Basic type-safe directed acyclic graphs.+Synopsis:               Compile-time, type-safe directed acyclic graphs. Description:   This is a type-safe approach for a directed acyclic graph.   .@@ -46,10 +46,10 @@   >   >  getSpanningTrees $ edges   >    :: Data.Proxy.Proxy-  >         '['Node "foo" '['Node "bar" '['Node "baz" '[]],-  >                         'Node "baz" '[]],-  >           'Node "bar" '['Node "baz" '[]],-  >           'Node "baz" '[]]+  >         '['Node "foo" '['Node "bar" '['Node "baz" '[]]+  >                        ,'Node "baz" '[]]+  >          ,'Node "bar" '['Node "baz" '[]]+  >          ,'Node "baz" '[]]   >   >  *Data.Graph.DAG> reflect $ getSpanningTrees $ edges   >@@ -58,8 +58,40 @@   >    ,Node "bar" [Node "baz" []]   >    ,Node "baz" []]   .+  We can also look at the edges, first-class:+  .+  >  *Data.Graph.DAG> fcEdges edges+  >+  >    [("foo","bar"),("foo","baz"),("bar","baz")]+  .+  Node construction is done with a uniquely keyed (inductive) map:+  .+  >  data Cool = AllRight | Radical | SuperDuper deriving (Show, Eq)+  >  nodes =+  >    nadd "foo" AllRight $+  >    nadd "bar" Radical $+  >    nadd "baz" SuperDuper $+  >    nempty+  .+  Note that a @NodeSchema@'s keys don't have to be in-sync with it's paired+  @EdgeSchema@. After we have both, we can construct a @DAG@:+  .+  >  graph = DAG edges nodes+  .+  Now we can do fun things, like get the spanning tree of a node:+  .+  >  *Data.Graph.DAG> gtree "foo" graph+  >+  >    Just (AllRight :@-> [Radical :@-> [SuperDuper :@-> []]+  >                        ,SuperDuper :@-> []])+  .   This library is still very naive, but it will give us compile-time enforcement   of acyclicity (and uniqueness) in these graphs - ideal for dependency graphs.+  .+  The main deficiency of this graph is that our @EdgeSchema@ can't be+  /deconstructed/ soundly - there is just too much information loss between the+  value and type levels. This means we can't delete edges or look inside, but we+  can still add edges or work with the resulting structure.  Cabal-Version:          >= 1.10 Build-Type:             Simple
src/Data/Graph/DAG.hs view
@@ -7,50 +7,40 @@ module Data.Graph.DAG         ( module Data.Graph.DAG.Edge         , module Data.Graph.DAG.Edge.Utils+        , module Data.Graph.DAG.Node         , DAG (..)         , glookup         ) where  import Data.Graph.DAG.Edge import Data.Graph.DAG.Edge.Utils+import Data.Graph.DAG.Node  import Data.List (lookup) import Data.Singletons import Data.Proxy---- | The graph may be not connected-data DAG es a = forall x unique. GNil (EdgeSchema es x unique)-              | GCons String a (DAG es a)--instance Functor (DAG es) where-  fmap f (GNil e) = GNil e-  fmap f (GCons k x xs) = GCons k (f x) $-    fmap f xs--{---- | Convenience function.--- getEdgeSchema :: DAG es-getEdgeSchema (GNil e) = e-getEdgeSchema (GCons _ _ gs) = getEdgeSchema gs--}+import Data.Maybe (fromJust) --- | A simple @Data.Map.lookup@ duplicate.-glookup :: String -> DAG es a -> Maybe a-glookup _ (GNil _) = Nothing-glookup k (GCons k2 a gs) | k == k2   = Just a-                          | otherwise = glookup k gs+-- | A (potentially sparse) directed acyclic graph, composed of edges and nodes.+data DAG es x u a = DAG { getEdgeSchema :: (EdgeSchema es x u)+                        , getNodeSchema :: (NodeSchema a)+                        } --- | Get the spanning trees of an @EdgeSchema@. Operate on the assumtion that--- the data returned is actually @[Tree String]@.-espanningtrees :: SingI (SpanningTrees' es '[]) =>-                  EdgeSchema es x unique-               -> Demote (SpanningTrees' es '[])-espanningtrees = reflect . getSpanningTrees+instance Functor (DAG es x u) where+  fmap f (DAG es xs) = DAG es $ fmap f xs --- gspanningtrees+-- | @Data.Map.lookup@ duplicate.+glookup :: String -> DAG es x u a -> Maybe a+glookup k (DAG _ xs) = nlookup k xs +-- | Spanning trees of a graph.+gspanningtrees :: SingI (SpanningTrees' es '[]) =>+                  DAG es x u a -> [RTree a]+gspanningtrees g = fmap replace $ espanningtrees $ getEdgeSchema g+  where+  replace = fmap $ fromJust . flip glookup g -{--gtree :: String -> DAG es a -> Maybe (Tree a)-gtree k g = lookup k $ force $ reflect $ getSpanningTrees $ getEdgeSchema g--}+-- | Spanning tree of a particular node. "A possible tree of possible results"+gtree :: SingI (SpanningTrees' es '[]) =>+         String -> DAG es x unique a -> Maybe (RTree a)+gtree k g = fmap (fmap $ fromJust . flip glookup g) $ etree k $ getEdgeSchema g
src/Data/Graph/DAG/Edge.hs view
@@ -76,15 +76,15 @@ type family DisallowIn               (new :: EdgeKind)               ( oldLoops :: [(Symbol, [Symbol])] )-              (keyFound :: Bool) :: [(Symbol, [Symbol])] where+              (keyFoundYet :: Bool) :: [(Symbol, [Symbol])] where -- When @from ~ key@:   DisallowIn ('EdgeType from to) ( '(from, xs) ': es) 'False =     '(from, (to ': xs)) ':                      -- add @to@ to transitive reach list       (DisallowIn ('EdgeType from to) es 'True) -- continue -- When @from ~/~ key@, and @from ~/~ head value@-  DisallowIn ('EdgeType from to) ( '(key, vs) ': es ) keyFound =+  DisallowIn ('EdgeType from to) ( '(key, vs) ': es ) keyFoundYet =     '(key, (PrependIfElem from to vs)) ':            -- find the needle if it exists-        (DisallowIn ('EdgeType from to) es keyFound) -- continue+        (DisallowIn ('EdgeType from to) es keyFoundYet) -- continue -- Basis   DisallowIn a '[] 'True = '[] -- search over. -- Growth via append@@ -104,7 +104,8 @@              -> !(EdgeSchema old oldLoops unique)              -> EdgeSchema (b ': old) c unique --- | Utility for constructing an @EdgeSchema@ granularly+-- | Utility for constructing an @EdgeSchema@ incrementally without a type+-- signature. unique :: EdgeSchema '[] '[] 'True unique = ENil 
src/Data/Graph/DAG/Edge/Utils.hs view
@@ -20,31 +20,35 @@ import Data.Singletons.Prelude import Data.Proxy import Data.Monoid+import Data.Foldable (Foldable (foldMap)) import Control.Applicative  -- | Trivial rose tree for creating spanning trees. We make control structure -- instances "parallel" (instead of cartesian) by default for simplicity. $(singletons [d|-  data Tree a = Node a [Tree a] deriving (Show, Eq, Functor)+  data RTree a = a :@-> [RTree a] deriving (Show, Eq, Functor)   |]) -instance Applicative Tree where-  pure a = Node a []-  (Node f fs) <*> (Node x xs) = Node (f x) $-    zipWith (<*>) fs xs+instance Applicative RTree where+  pure a = a :@-> []+  (f :@-> fs) <*> (x :@-> xs) = (f x) :@->+    (zipWith (<*>) fs xs) -instance Monad Tree where+instance Monad RTree where   return = pure-  (Node x xs) >>= f = case f x of -- Substitution based instance.-    (Node y ys) -> Node y $ fmap (>>= f) xs+  (x :@-> xs) >>= f = case f x of -- Substitution based instance.+    (y :@-> ys) -> y :@-> (fmap (>>= f) xs) -instance Monoid a => Monoid (Tree a) where-  mempty = Node mempty []-  (Node x xs) `mappend` (Node y ys) = Node (x `mappend` y) $-    zipWith mappend xs ys+instance Monoid a => Monoid (RTree a) where+  mempty = mempty :@-> []+  (x :@-> xs) `mappend` (y :@-> ys) = (x `mappend` y) :@->+    (zipWith mappend xs ys) +instance Foldable RTree where+  foldMap f (x :@-> xs) = f x <> foldMap (foldMap f) xs+ -- | Gives us a generic way to get our spanning trees of the graph, as a value.--- Credit goes to <stackoverflow.com/questions/28030118/reflecting-heterogeneous-promoted-types-back-to-values-compositionally András Kovács>.+-- Credit goes to <http://stackoverflow.com/questions/28030118/reflecting-heterogeneous-promoted-types-back-to-values-compositionally András Kovács>. reflect ::   forall (a :: k).   (SingI a, SingKind ('KProxy :: KProxy k)) =>@@ -52,66 +56,123 @@ reflect _ = fromSing (sing :: Sing a)  -- | Adds an empty @c@ tree to the list of trees uniquely-type family AppendIfNotElemTrees (c :: k) (trees :: [Tree k]) :: [Tree k] where-  AppendIfNotElemTrees c ((Node c xs) ': xss) = (Node c xs) ': xss-  AppendIfNotElemTrees c ((Node x xs) ': xss) = (Node x xs) ':+type family AppendIfNotElemTrees (c :: k) (trees :: [RTree k]) :: [RTree k] where+  AppendIfNotElemTrees c ((c :@-> xs) ': xss) = (c :@-> xs) ': xss+  AppendIfNotElemTrees c ((x :@-> xs) ': xss) = (x :@-> xs) ':     (AppendIfNotElemTrees c xss)-  AppendIfNotElemTrees c '[] = (Node c '[]) ': '[]+  AppendIfNotElemTrees c '[] = (c :@-> '[]) ': '[]  -- | Adds @c@ as a child of any tree with a root @t@. Assumes unique roots. type family AddChildTo (test :: k)                        (child :: k)-                       (trees :: [Tree k]) :: [Tree k] where-  AddChildTo t c ((Node t xs) ': xss) =-    (Node t (AppendIfNotElemTrees c xs)) ': (AddChildTo t c xss)-  AddChildTo t c ((Node x xs) ': xss) =-    (Node x (AddChildTo t c xs)) ': (AddChildTo t c xss)+                       (trees :: [RTree k]) :: [RTree k] where+  AddChildTo t c ((t :@-> xs) ': xss) =+    (t :@-> (AppendIfNotElemTrees c xs)) ': (AddChildTo t c xss)+  AddChildTo t c ((x :@-> xs) ': xss) =+    (x :@-> (AddChildTo t c xs)) ': (AddChildTo t c xss)   AddChildTo t c '[] = '[]  -- | We need to track if @from@ has is a root node or not. TODO: Some code repeat. type family AddEdge' (edge :: EdgeKind)-                     (trees :: [Tree Symbol])+                     (trees :: [RTree Symbol])                      (hasFromRoot :: Bool)-                     (hasToRoot :: Bool):: [Tree Symbol] where+                     (hasToRoot :: Bool):: [RTree Symbol] where   AddEdge' ('EdgeType from to) '[] 'False 'False =-    (Node from ((Node to '[]) ': '[])) ': (Node to '[]) ': '[]+    (from :@-> ((to :@-> '[]) ': '[])) ': (to :@-> '[]) ': '[]    AddEdge' ('EdgeType from to) '[] 'True 'False =-    (Node to                     '[])  ':                  '[]+    (to :@->                     '[])  ':                  '[]    AddEdge' ('EdgeType from to) '[] 'False 'True =-    (Node from ((Node to '[]) ': '[])) ':                  '[]+    (from :@-> ((to :@-> '[]) ': '[])) ':                  '[]    AddEdge' x '[] 'True 'True = '[] -  AddEdge' ('EdgeType from to) ((Node from xs) ': xss) hasFromRoot hasToRoot =-    (Node from (AppendIfNotElemTrees to xs)) ':+  AddEdge' ('EdgeType from to) ((from :@-> xs) ': xss) hasFromRoot hasToRoot =+    (from :@-> (AppendIfNotElemTrees to xs)) ':       (AddEdge' ('EdgeType from to) xss 'True hasToRoot) -  AddEdge' ('EdgeType from to) ((Node to xs) ': xss) hasFromRoot hasToRoot =-    (Node to (AddEdge' ('EdgeType from to) xs 'True 'True)) ':+  AddEdge' ('EdgeType from to) ((to :@-> xs) ': xss) hasFromRoot hasToRoot =+    (to :@-> (AddEdge' ('EdgeType from to) xs 'True 'True)) ':       (AddEdge' ('EdgeType from to) xss hasFromRoot 'True)    -- Go downward, and laterally (I think).-  AddEdge' ('EdgeType from to) ((Node x xs) ': xss) hasFromRoot hasToRoot =-    (Node x (AddEdge' ('EdgeType from to) xs 'True 'True)) ':+  AddEdge' ('EdgeType from to) ((x :@-> xs) ': xss) hasFromRoot hasToRoot =+    (x :@-> (AddEdge' ('EdgeType from to) xs 'True 'True)) ':       (AddEdge' ('EdgeType from to) xss hasFromRoot hasToRoot)  -- | Add @to@ as a child to every @from@ node in the accumulator. type family AddEdge (edge :: EdgeKind)-                    (trees :: [Tree Symbol]) :: [Tree Symbol] where+                    (trees :: [RTree Symbol]) :: [RTree Symbol] where   AddEdge a trees = AddEdge' a trees 'False 'False  -- | Auxilliary function normally defined in a @where@ clause for manual folding. type family SpanningTrees' (edges :: [EdgeKind])-                           (acc :: [Tree Symbol]) :: [Tree Symbol] where+                           (acc :: [RTree Symbol]) :: [RTree Symbol] where   SpanningTrees' '[] trees = trees   SpanningTrees' (('EdgeType from to) ': es) trees =     SpanningTrees' es (AddEdge ('EdgeType from to) trees)  -- | Expects edges to already be type-safe-type family SpanningTrees (edges :: [EdgeKind]) :: [Tree Symbol] where+type family SpanningTrees (edges :: [EdgeKind]) :: [RTree Symbol] where   SpanningTrees edges = SpanningTrees' edges '[]  getSpanningTrees :: EdgeSchema es x unique -> Proxy (SpanningTrees es) getSpanningTrees _ = Proxy++-- | Get the spanning trees of an @EdgeSchema@. Operate on the assumtion that+-- the data returned is actually @[Tree String]@.+espanningtrees :: SingI (SpanningTrees' es '[]) =>+                  EdgeSchema es x unique+               -> Demote (SpanningTrees' es '[])+espanningtrees = reflect . getSpanningTrees++-- | Get a single tree.+etree :: SingI (SpanningTrees' es '[]) =>+         String -> EdgeSchema es x unique -> Maybe (RTree String)+etree k es = getTree k $ espanningtrees es+  where+  getTree k1 ( n@(k2 :@-> xs) : ns ) | k1 == k2 = Just n+                                     | otherwise = getTree k1 ns+  getTree _ [] = Nothing++-- | Degenerate (but type-safe!) @head@.+ehead :: ( EdgeType from to ~ b+         , EdgeValue from to ~ a+         ) => EdgeSchema (b ': old) c u -> a+ehead _ = Edge++-- | For now, we only suport unique edges.+eTreeToEdges :: RTree String -> [(String,String)]+eTreeToEdges = treeToEdges' []+  where+  treeToEdges' :: [(String,String)]+               -> RTree String+               -> [(String,String)]+  treeToEdges' zs (_ :@-> []) = zs+  treeToEdges' zs (x :@-> xs) =+    let newEdges = umerge zs $ map (\q -> (x, getNodeVal q)) xs+    in+    foldl treeToEdges' newEdges xs+  getNodeVal (x :@-> _) = x+  -- unique merge+  umerge [] ys = ys+  umerge (x:xs) ys | x `elem` ys = umerge xs ys+                   | otherwise = x : umerge xs ys++-- | Get a first-class list of edges from spanning trees. Only works on uniqely+-- edged @EdgeSchema@'s.+eForestToEdges :: [RTree String] -> [(String,String)]+eForestToEdges xs = foldl (\es t -> umerge es $ eTreeToEdges t) [] xs+  where+  -- unique merge+  umerge [] ys = ys+  umerge (x:xs) ys | x `elem` ys = umerge xs ys+                   | otherwise = x : umerge xs ys++-- | Get the "First-Class" edges of a uniquely-edged @EdgeSchema@.+fcEdges :: SingI (SpanningTrees' es '[]) =>+           EdgeSchema es x 'True -> [(String, String)]+fcEdges = eForestToEdges . espanningtrees++-- eflip e = espanningtrees e