module Data.Deps (
Deps(..), depsMap,
mapDeps,
dep, deps,
inverse,
DepsError(..), flatten,
selfDepend,
linearize
) where
import Control.Lens
import Control.Monad.State
import Control.Monad.Except
import Data.List (nub, intercalate)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe)
newtype Deps a = Deps {
_depsMap :: Map a [a] }
depsMap :: Lens (Deps a) (Deps b) (Map a [a]) (Map b [b])
depsMap = lens _depsMap (const Deps)
instance Ord a => Monoid (Deps a) where
mempty = Deps mempty
mappend (Deps l) (Deps r) = Deps $ M.unionWith nubConcat l r
instance Show a => Show (Deps a) where
show (Deps ds) = unlines [show d ++ " -> " ++ intercalate ", " (map show s) | (d, s) <- M.toList ds]
type instance Index (Deps a) = a
type instance IxValue (Deps a) = [a]
instance Ord a => Ixed (Deps a) where
ix k = depsMap . ix k
instance Ord a => At (Deps a) where
at k = depsMap . at k
mapDeps :: Ord b => (a -> b) -> Deps a -> Deps b
mapDeps f = Deps . M.mapKeys f . M.map (map f) . _depsMap
dep :: a -> a -> Deps a
dep x y = deps x [y]
deps :: a -> [a] -> Deps a
deps x ys = Deps $ M.singleton x ys
inverse :: Ord a => Deps a -> Deps a
inverse = mconcat . map (uncurry dep) . concatMap inverse' . M.toList . _depsMap where
inverse' :: (a, [a]) -> [(a, a)]
inverse' (m, ds) = zip ds (repeat m)
newtype DepsError a =
CyclicDeps [a]
deriving (Eq, Ord, Read)
instance Show a => Show (DepsError a) where
show (CyclicDeps c) = "dependencies forms a cycle: " ++ concat [show d ++ " -> " | d <- c] ++ "..."
flatten :: Ord a => Deps a -> Either (DepsError a) (Deps a)
flatten s@(Deps ds) = fmap snd . flip execStateT mempty . mapM_ (flatten' s) . M.keys $ ds where
flatten' :: Ord a => Deps a -> a -> StateT ([a], Deps a) (Either (DepsError a)) [a]
flatten' s' n = do
path <- gets (view _1)
when (preview (reversed . each) path == Just n) $ throwError (CyclicDeps $ reverse path)
d <- gets (preview $ _2 . ix n)
case d of
Just d' -> return d'
Nothing -> pushPath n $ do
d'' <- (nub . concat . (++ [deps'])) <$> mapM (flatten' s') deps'
modify (over _2 $ mappend (deps n d''))
return d''
where
deps' = fromMaybe [] $ preview (ix n) s'
pushPath :: MonadState ([a], Deps a) m => a -> m b -> m b
pushPath p act = do
modify (over _1 (p:))
r <- act
modify (over _1 tail)
return r
selfDepend :: Deps a -> Deps a
selfDepend = Deps . M.mapWithKey (\s d -> d ++ [s]) . _depsMap
linearize :: Ord a => Deps a -> Either (DepsError a) [a]
linearize = fmap (nub . concat . toListOf (depsMap . each) . selfDepend) . flatten
nubConcat :: Ord a => [a] -> [a] -> [a]
nubConcat xs ys = nub $ xs ++ ys