dag 0.0.1 → 0.0.2
raw patch · 4 files changed
+144/−85 lines, 4 filesdep +singletonsPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: singletons
API changes (from Hackage documentation)
- Data.Graph.DAG.Edge: Node :: a -> [Tree a] -> Tree a
- Data.Graph.DAG.Edge: data Tree a
- Data.Graph.DAG.Edge: getSpanningTrees :: EdgeSchema es x unique -> Proxy (SpanningTrees es)
+ Data.Graph.DAG.Edge.Utils: Node :: a_aa2d -> [Tree a_aa2d] -> Tree a_aa2d
+ Data.Graph.DAG.Edge.Utils: NodeSym0KindInference :: NodeSym0
+ Data.Graph.DAG.Edge.Utils: NodeSym1KindInference :: NodeSym1
+ Data.Graph.DAG.Edge.Utils: data NodeSym0 (l_aa2r :: TyFun a_aa2d (TyFun ([] (Tree a_aa2d)) (Tree a_aa2d) -> *))
+ Data.Graph.DAG.Edge.Utils: data NodeSym1 (l_aa2u :: a_aa2d) (l_aa2t :: TyFun ([] (Tree a_aa2d)) (Tree a_aa2d))
+ Data.Graph.DAG.Edge.Utils: data Tree a_aa2d
+ Data.Graph.DAG.Edge.Utils: getSpanningTrees :: EdgeSchema es x unique -> Proxy (SpanningTrees es)
+ Data.Graph.DAG.Edge.Utils: instance (SingI n0, SingI n1) => SingI ('Node n0 n1)
+ Data.Graph.DAG.Edge.Utils: instance Eq a0 => Eq (Tree a0)
+ Data.Graph.DAG.Edge.Utils: instance PEq 'KProxy
+ Data.Graph.DAG.Edge.Utils: instance SDecide 'KProxy => SDecide 'KProxy
+ Data.Graph.DAG.Edge.Utils: instance SEq 'KProxy => SEq 'KProxy
+ Data.Graph.DAG.Edge.Utils: instance Show a0 => Show (Tree a0)
+ Data.Graph.DAG.Edge.Utils: instance SingKind 'KProxy => SingKind 'KProxy
+ Data.Graph.DAG.Edge.Utils: instance SuppressUnusedWarnings NodeSym0
+ Data.Graph.DAG.Edge.Utils: instance SuppressUnusedWarnings NodeSym1
+ Data.Graph.DAG.Edge.Utils: reflect :: (SingI a, SingKind (KProxy :: KProxy k)) => Proxy a -> Demote a
+ Data.Graph.DAG.Edge.Utils: type NodeSym2 (t_aa2p :: a_aa2d) (t_aa2q :: [] (Tree a_aa2d)) = Node t_aa2p t_aa2q
+ Data.Graph.DAG.Edge.Utils: type STree (z_aa2w :: Tree a_aa2d) = Sing z_aa2w
- Data.Graph.DAG: GCons :: key -> a -> DAG es a -> DAG es a
+ Data.Graph.DAG: GCons :: String -> a -> DAG es a -> DAG es a
- Data.Graph.DAG.Edge: ECons :: !a -> EdgeSchema old oldLoops unique -> EdgeSchema (b : old) c unique
+ Data.Graph.DAG.Edge: ECons :: !a -> !(EdgeSchema old oldLoops unique) -> EdgeSchema (b : old) c unique
Files
- dag.cabal +20/−11
- src/Data/Graph/DAG.hs +23/−5
- src/Data/Graph/DAG/Edge.hs +2/−69
- src/Data/Graph/DAG/Edge/Utils.hs +99/−0
dag.cabal view
@@ -1,5 +1,5 @@ Name: dag-Version: 0.0.1+Version: 0.0.2 Author: Athan Clark <athan.clark@gmail.com> Maintainer: Athan Clark <athan.clark@gmail.com> License: BSD3@@ -8,48 +8,55 @@ Description: This is a type-safe approach for a directed acyclic graph. .- Edge construction is inductive, creating a "schema":+ Edge construction is incremental, creating a "schema": . > import Data.Graph.DAG.Edge > > -- | Edges are statically defined:- > edges = ECons (Edge :: EdgeValue "foo" "bar") $+ > edges =+ > ECons (Edge :: EdgeValue "foo" "bar") $ > ECons (Edge :: EdgeValue "bar" "baz") $ > ECons (Edge :: EdgeValue "foo" "baz")- > unique -- ENil, but for uniquely edged graphs+ > unique -- ENil, but casted for uniquely edged graphs .- Which we use to populate nodes with values:+ The nodes are separate from edges; graph may be not connected: . > data Cool = AllRight > | Radical > | SuperDuper >- > graph = GCons "foo" AllRight $+ > graph =+ > GCons "foo" AllRight $ > GCons "bar" Radical $ > GCons "baz" SuperDuper $ > GNil edges .- It's an instance of `Functor`, but we haven't done much here - it will require- a lot of reflection that I don't have time to implement right now - there isn't- even binding of value-based `GCons` keys and `ECons` edge node labels.- . Some type tomfoolery: . > *Data.Graph.DAG> :t edges+ > > edges > :: EdgeSchema > '['EdgeType "foo" "bar", 'EdgeType "bar" "baz", > 'EdgeType "foo" "baz"] -- Type list of edges > '['("foo", '["bar", "baz"]), '("bar", '["baz"])] -- potential loops > 'True -- uniqueness- .+ > > *Data.Graph.DAG> :t getSpanningTrees $ edges+ > > getSpanningTrees $ edges > :: Data.Proxy.Proxy > '['Node "foo" '['Node "bar" '['Node "baz" '[]], > 'Node "baz" '[]], > 'Node "bar" '['Node "baz" '[]], > 'Node "baz" '[]]+ >+ > *Data.Graph.DAG> reflect $ getSpanningTrees $ edges+ >+ > [Node "foo" [Node "bar" [Node "baz" []]+ > ,Node "baz" []]+ > ,Node "bar" [Node "baz" []]+ > ,Node "baz" []] . 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.@@ -63,8 +70,10 @@ GHC-Options: -Wall Exposed-Modules: Data.Graph.DAG Data.Graph.DAG.Edge+ Data.Graph.DAG.Edge.Utils Build-Depends: base >= 4 && < 5 , constraints+ , singletons Test-Suite spec Type: exitcode-stdio-1.0
src/Data/Graph/DAG.hs view
@@ -1,29 +1,47 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE DataKinds #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE ScopedTypeVariables #-} module Data.Graph.DAG ( module Data.Graph.DAG.Edge+ , module Data.Graph.DAG.Edge.Utils , DAG (..) , glookup ) where import Data.Graph.DAG.Edge+import Data.Graph.DAG.Edge.Utils +import Data.List (lookup)++-- | The graph may be not connected data DAG es a where- GNil :: EdgeSchema es x unique -> DAG es a- GCons :: (String ~ key) =>- key+ GNil :: forall es a x unique. EdgeSchema es x unique+ -> DAG es a+ GCons :: String -> a -- value -> DAG es a -> DAG es a-+{-+-- | Convenience function.+getEdgeSchema :: DAG es a -> EdgeSchema es x unique+getEdgeSchema (GNil e) = (e :: EdgeSchema es x unique)+getEdgeSchema (GCons _ _ gs) = getEdgeSchema gs+-} instance Functor (DAG es) where fmap f (GNil e) = GNil e fmap f (GCons k x xs) = GCons k (f x) $ fmap f xs -+-- | 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+++{-+gtree :: String -> DAG es a -> Maybe (Tree a)+gtree k g = lookup k $ force $ reflect $ getSpanningTrees $ getEdgeSchema g+-}
src/Data/Graph/DAG/Edge.hs view
@@ -11,6 +11,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FunctionalDependencies #-} module Data.Graph.DAG.Edge where @@ -100,7 +101,7 @@ , EdgeType from to ~ b , DisallowIn b oldLoops 'False ~ c ) => !a- -> EdgeSchema old oldLoops unique+ -> !(EdgeSchema old oldLoops unique) -> EdgeSchema (b ': old) c unique -- | Utility for constructing an @EdgeSchema@ granularly@@ -109,71 +110,3 @@ notUnique :: EdgeSchema '[] '[] 'False notUnique = ENil---- | Trivial rose tree for creating spanning trees-data Tree a = Node a [Tree 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) ':- (AppendIfNotElemTrees c xss)- AppendIfNotElemTrees c '[] = (Node 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)- 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])- (hasFromRoot :: Bool)- (hasToRoot :: Bool):: [Tree Symbol] where- AddEdge' ('EdgeType from to) '[] 'False 'False =- (Node from ((Node to '[]) ': '[])) ': (Node to '[]) ': '[]-- AddEdge' ('EdgeType from to) '[] 'True 'False =- (Node to '[]) ': '[]-- AddEdge' ('EdgeType from to) '[] 'False 'True =- (Node from ((Node to '[]) ': '[])) ': '[]-- AddEdge' x '[] 'True 'True = '[]-- AddEdge' ('EdgeType from to) ((Node from xs) ': xss) hasFromRoot hasToRoot =- (Node 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) 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) 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- 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- 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- SpanningTrees edges = SpanningTrees' edges '[]--getSpanningTrees :: EdgeSchema es x unique -> Proxy (SpanningTrees es)-getSpanningTrees _ = Proxy
+ src/Data/Graph/DAG/Edge/Utils.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UndecidableInstances #-}++module Data.Graph.DAG.Edge.Utils where++import Data.Graph.DAG.Edge++import GHC.TypeLits+import Data.Singletons.TH+import Data.Singletons.Prelude+import Data.Proxy+++-- | Trivial rose tree for creating spanning trees+$(singletons [d|+ data Tree a = Node a [Tree a] deriving (Show, Eq)+ |])++-- | 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>.+reflect ::+ forall (a :: k).+ (SingI a, SingKind ('KProxy :: KProxy k)) =>+ Proxy a -> Demote a+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) ':+ (AppendIfNotElemTrees c xss)+ AppendIfNotElemTrees c '[] = (Node 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)+ 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])+ (hasFromRoot :: Bool)+ (hasToRoot :: Bool):: [Tree Symbol] where+ AddEdge' ('EdgeType from to) '[] 'False 'False =+ (Node from ((Node to '[]) ': '[])) ': (Node to '[]) ': '[]++ AddEdge' ('EdgeType from to) '[] 'True 'False =+ (Node to '[]) ': '[]++ AddEdge' ('EdgeType from to) '[] 'False 'True =+ (Node from ((Node to '[]) ': '[])) ': '[]++ AddEdge' x '[] 'True 'True = '[]++ AddEdge' ('EdgeType from to) ((Node from xs) ': xss) hasFromRoot hasToRoot =+ (Node 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) 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) 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+ 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+ 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+ SpanningTrees edges = SpanningTrees' edges '[]++getSpanningTrees :: EdgeSchema es x unique -> Proxy (SpanningTrees es)+getSpanningTrees _ = Proxy