Commit 6d150d52 authored by Björn Peemöller 's avatar Björn Peemöller
Browse files

Improved error handling

parent 515afbcd
...@@ -36,7 +36,7 @@ Executable cymake ...@@ -36,7 +36,7 @@ Executable cymake
Build-Depends: base == 3.* Build-Depends: base == 3.*
Build-Depends: Build-Depends:
curry-base == 0.3.8 curry-base == 0.3.8
, containers, either, mtl, pretty, transformers , containers, either, mtl, transformers
ghc-options: -Wall ghc-options: -Wall
Other-Modules: Other-Modules:
Base.CurryTypes Base.CurryTypes
......
...@@ -6,9 +6,12 @@ module Base.Messages ...@@ -6,9 +6,12 @@ module Base.Messages
, internalError, errorMessage, errorMessages , internalError, errorMessage, errorMessages
-- * creating messages -- * creating messages
, Message, message, posMessage , Message, message, posMessage
, MonadIO (..), CYIO, CYT, left, right, runEitherCYIO
) where ) where
import Control.Monad (unless, when) import Control.Monad (unless, when)
import Control.Monad.IO.Class
import Control.Monad.Trans.Either
import Data.List (sort) import Data.List (sort)
import System.IO (hPutStrLn, stderr) import System.IO (hPutStrLn, stderr)
import System.Exit (exitFailure) import System.Exit (exitFailure)
...@@ -16,33 +19,46 @@ import System.Exit (exitFailure) ...@@ -16,33 +19,46 @@ import System.Exit (exitFailure)
import Curry.Base.Message hiding (warn) import Curry.Base.Message hiding (warn)
import CompilerOpts (Options (..), Verbosity (..)) import CompilerOpts (Options (..), Verbosity (..))
info :: Options -> String -> IO () type CYT m a = EitherT [Message] m a
info opts msg = unless (optVerbosity opts < VerbInfo)
(putStrLn $ msg ++ " ...")
status :: Options -> String -> IO () type CYIO a = EitherT [Message] IO a
status opts msg = unless (optVerbosity opts < VerbStatus)
(putStrLn $ msg ++ " ...")
warn :: Options -> [Message] -> IO () runEitherCYIO :: CYIO a -> IO a
runEitherCYIO act = do
res <- runEitherT act
case res of
Left errs -> abortWithMessages errs
Right val -> return val
info :: MonadIO m => Options -> String -> m ()
info opts msg = unless (optVerbosity opts < VerbInfo) (putMsg msg)
status :: MonadIO m => Options -> String -> m ()
status opts msg = unless (optVerbosity opts < VerbStatus) (putMsg msg)
warn :: MonadIO m => Options -> [Message] -> m ()
warn opts msgs = when (optWarn opts && not (null msgs)) $ do warn opts msgs = when (optWarn opts && not (null msgs)) $ do
putErrLn (show $ ppMessages ppWarning $ sort msgs) liftIO $ putErrLn (show $ ppMessages ppWarning $ sort msgs)
when (optWarnAsError opts) $ do when (optWarnAsError opts) $ liftIO $ do
putErrLn "Failed due to -Werror" putErrLn "Failed due to -Werror"
exitFailure exitFailure
-- |Print a message on 'stdout'
putMsg :: MonadIO m => String -> m ()
putMsg msg = liftIO $ putStrLn $ msg ++ " ..."
-- |Print an error message on 'stderr' -- |Print an error message on 'stderr'
putErrLn :: String -> IO () putErrLn :: MonadIO m => String -> m ()
putErrLn = hPutStrLn stderr putErrLn = liftIO . hPutStrLn stderr
-- |Print a list of error messages on 'stderr' -- |Print a list of error messages on 'stderr'
putErrsLn :: [String] -> IO () putErrsLn :: MonadIO m => [String] -> m ()
putErrsLn = mapM_ putErrLn putErrsLn = mapM_ putErrLn
-- |Print a list of 'String's as error messages on 'stderr' -- |Print a list of 'String's as error messages on 'stderr'
-- and abort the program -- and abort the program
abortWith :: [String] -> IO a abortWith :: [String] -> IO a
abortWith errs = unless (null errs) (putErrsLn errs) >> exitFailure abortWith errs = putErrsLn errs >> exitFailure
-- |Print a single error message on 'stderr' and abort the program -- |Print a single error message on 'stderr' and abort the program
abortWithMessage :: Message -> IO a abortWithMessage :: Message -> IO a
......
...@@ -13,11 +13,8 @@ ...@@ -13,11 +13,8 @@
-} -}
module Checks where module Checks where
import Control.Monad.Trans.Either
import Curry.Syntax (Module (..)) import Curry.Syntax (Module (..))
import Base.Messages
import qualified Checks.ExportCheck as EC (exportCheck) import qualified Checks.ExportCheck as EC (exportCheck)
import qualified Checks.KindCheck as KC (kindCheck) import qualified Checks.KindCheck as KC (kindCheck)
import qualified Checks.PrecCheck as PC (precCheck) import qualified Checks.PrecCheck as PC (precCheck)
...@@ -25,18 +22,18 @@ import qualified Checks.SyntaxCheck as SC (syntaxCheck) ...@@ -25,18 +22,18 @@ import qualified Checks.SyntaxCheck as SC (syntaxCheck)
import qualified Checks.TypeCheck as TC (typeCheck) import qualified Checks.TypeCheck as TC (typeCheck)
import qualified Checks.WarnCheck as WC (warnCheck) import qualified Checks.WarnCheck as WC (warnCheck)
import Base.Messages
import CompilerEnv import CompilerEnv
import CompilerOpts import CompilerOpts
type Check m = Options -> CompilerEnv -> Module type Check m a = Options -> CompilerEnv -> a -> CYT m (CompilerEnv, a)
-> EitherT [Message] m (CompilerEnv, Module)
-- |Check the kinds of type definitions and signatures. -- |Check the kinds of type definitions and signatures.
-- --
-- * Declarations: Nullary type constructors and type variables are -- * Declarations: Nullary type constructors and type variables are
-- disambiguated -- disambiguated
-- * Environment: remains unchanged -- * Environment: remains unchanged
kindCheck :: Monad m => Check m kindCheck :: Monad m => Check m Module
kindCheck _ env (Module m es is ds) kindCheck _ env (Module m es is ds)
| null msgs = right (env, Module m es is ds') | null msgs = right (env, Module m es is ds')
| otherwise = left msgs | otherwise = left msgs
...@@ -47,7 +44,7 @@ kindCheck _ env (Module m es is ds) ...@@ -47,7 +44,7 @@ kindCheck _ env (Module m es is ds)
-- * Declarations: Nullary data constructors and variables are -- * Declarations: Nullary data constructors and variables are
-- disambiguated, variables are renamed -- disambiguated, variables are renamed
-- * Environment: remains unchanged -- * Environment: remains unchanged
syntaxCheck :: Monad m => Check m syntaxCheck :: Monad m => Check m Module
syntaxCheck opts env (Module m es is ds) syntaxCheck opts env (Module m es is ds)
| null msgs = right (env, Module m es is ds') | null msgs = right (env, Module m es is ds')
| otherwise = left msgs | otherwise = left msgs
...@@ -59,7 +56,7 @@ syntaxCheck opts env (Module m es is ds) ...@@ -59,7 +56,7 @@ syntaxCheck opts env (Module m es is ds)
-- * Declarations: Expressions are reordered according to the specified -- * Declarations: Expressions are reordered according to the specified
-- precedences -- precedences
-- * Environment: The operator precedence environment is updated -- * Environment: The operator precedence environment is updated
precCheck :: Monad m => Check m precCheck :: Monad m => Check m Module
precCheck _ env (Module m es is ds) precCheck _ env (Module m es is ds)
| null msgs = right (env { opPrecEnv = pEnv' }, Module m es is ds') | null msgs = right (env { opPrecEnv = pEnv' }, Module m es is ds')
| otherwise = left msgs | otherwise = left msgs
...@@ -68,7 +65,7 @@ precCheck _ env (Module m es is ds) ...@@ -68,7 +65,7 @@ precCheck _ env (Module m es is ds)
-- |Apply the correct typing of the module. -- |Apply the correct typing of the module.
-- The declarations remain unchanged; the type constructor and value -- The declarations remain unchanged; the type constructor and value
-- environments are updated. -- environments are updated.
typeCheck :: Monad m => Check m typeCheck :: Monad m => Check m Module
typeCheck _ env mdl@(Module _ _ _ ds) typeCheck _ env mdl@(Module _ _ _ ds)
| null msgs = right (env { tyConsEnv = tcEnv', valueEnv = tyEnv' }, mdl) | null msgs = right (env { tyConsEnv = tcEnv', valueEnv = tyEnv' }, mdl)
| otherwise = left msgs | otherwise = left msgs
...@@ -76,7 +73,7 @@ typeCheck _ env mdl@(Module _ _ _ ds) ...@@ -76,7 +73,7 @@ typeCheck _ env mdl@(Module _ _ _ ds)
(tyConsEnv env) (valueEnv env) ds (tyConsEnv env) (valueEnv env) ds
-- |Check the export specification -- |Check the export specification
exportCheck :: Monad m => Check m exportCheck :: Monad m => Check m Module
exportCheck _ env (Module m es is ds) exportCheck _ env (Module m es is ds)
| null msgs = right (env, Module m es' is ds) | null msgs = right (env, Module m es' is ds)
| otherwise = left msgs | otherwise = left msgs
......
module Checks.ExportCheck (exportCheck) where module Checks.ExportCheck (exportCheck) where
import Control.Monad (liftM, unless) import Control.Monad (liftM, unless)
import qualified Control.Monad.State as S (State, runState, gets, modify) import qualified Control.Monad.State as S (State, runState, gets, modify)
import Data.List (nub, union) import Data.List (nub, union)
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import qualified Data.Set as Set import qualified Data.Set as Set
import Text.PrettyPrint
import Curry.Base.Ident import Curry.Base.Ident
import Curry.Base.Position import Curry.Base.Position
import Curry.Base.Pretty
import Curry.Syntax import Curry.Syntax
import Base.Messages (Message, internalError, posMessage) import Base.Messages (Message, internalError, posMessage)
......
...@@ -25,12 +25,12 @@ is defined more than once. ...@@ -25,12 +25,12 @@ is defined more than once.
> module Checks.KindCheck (kindCheck) where > module Checks.KindCheck (kindCheck) where
> import Control.Monad (forM, liftM, liftM2, liftM3, unless, when) > import Control.Monad (forM, liftM, liftM2, liftM3, unless, when)
> import qualified Control.Monad.State as S (State, runState, gets, modify) > import qualified Control.Monad.State as S (State, runState, gets, modify)
> import Text.PrettyPrint
> import Curry.Base.Ident > import Curry.Base.Ident
> import Curry.Base.Position > import Curry.Base.Position
> import Curry.Base.Pretty
> import Curry.Syntax > import Curry.Syntax
> import Base.Messages (Message, posMessage, internalError) > import Base.Messages (Message, posMessage, internalError)
......
...@@ -18,13 +18,13 @@ of the operators involved. ...@@ -18,13 +18,13 @@ of the operators involved.
> module Checks.PrecCheck (precCheck) where > module Checks.PrecCheck (precCheck) where
> import Control.Monad (liftM, liftM2, liftM3, unless, when) > import Control.Monad (liftM, liftM2, liftM3, unless, when)
> import qualified Control.Monad.State as S (State, runState, gets, modify) > import qualified Control.Monad.State as S (State, runState, gets, modify)
> import Data.List (partition) > import Data.List (partition)
> import Text.PrettyPrint
> import Curry.Base.Ident > import Curry.Base.Ident
> import Curry.Base.Position > import Curry.Base.Position
> import Curry.Base.Pretty
> import Curry.Syntax > import Curry.Syntax
> import Base.Expr > import Base.Expr
......
...@@ -27,10 +27,10 @@ definition. ...@@ -27,10 +27,10 @@ definition.
> import Data.List ((\\), insertBy, nub, partition) > import Data.List ((\\), insertBy, nub, partition)
> import Data.Maybe (fromJust, isJust, isNothing, maybeToList) > import Data.Maybe (fromJust, isJust, isNothing, maybeToList)
> import qualified Data.Set as Set (empty, insert, member) > import qualified Data.Set as Set (empty, insert, member)
> import Text.PrettyPrint
> import Curry.Base.Ident > import Curry.Base.Ident
> import Curry.Base.Position > import Curry.Base.Position
> import Curry.Base.Pretty
> import Curry.Syntax > import Curry.Syntax
> import Curry.Syntax.Pretty (ppPattern) > import Curry.Syntax.Pretty (ppPattern)
......
...@@ -21,10 +21,10 @@ import qualified Data.Map as Map (empty, insert, lookup) ...@@ -21,10 +21,10 @@ import qualified Data.Map as Map (empty, insert, lookup)
import Data.Maybe (catMaybes, isJust) import Data.Maybe (catMaybes, isJust)
import Data.List import Data.List
(intersect, intersectBy, nub, partition, sort, unionBy) (intersect, intersectBy, nub, partition, sort, unionBy)
import Text.PrettyPrint
import Curry.Base.Ident import Curry.Base.Ident
import Curry.Base.Position import Curry.Base.Position
import Curry.Base.Pretty
import Curry.Syntax import Curry.Syntax
import Curry.Syntax.Pretty (ppPattern, ppExpr, ppIdent) import Curry.Syntax.Pretty (ppPattern, ppExpr, ppIdent)
......
...@@ -14,9 +14,9 @@ ...@@ -14,9 +14,9 @@
module CompilerEnv where module CompilerEnv where
import qualified Data.Map as Map (Map, keys, toList) import qualified Data.Map as Map (Map, keys, toList)
import Text.PrettyPrint
import Curry.Base.Ident (ModuleIdent) import Curry.Base.Ident (ModuleIdent)
import Curry.Base.Pretty
import Base.TopEnv (allLocalBindings) import Base.TopEnv (allLocalBindings)
......
{- | {- |
Module : $Header$ Module : $Header$
Description : Build tool for compiling multiple Curry modules Description : Build tool for compiling multiple Curry modules
Copyright : (c) 2005, Martin Engelke (men@informatik.uni-kiel.de) Copyright : (c) 2005 Martin Engelke
2007, Sebastian Fischer (sebf@informatik.uni-kiel.de) 2007 Sebastian Fischer
2011, Björn Peemöller (bjp@informatik.uni-kiel.de) 2011 - 2013 Björn Peemöller
License : OtherLicense License : OtherLicense
Maintainer : bjp@informatik.uni-kiel.de Maintainer : bjp@informatik.uni-kiel.de
...@@ -18,14 +18,13 @@ module CurryBuilder (buildCurry, smake) where ...@@ -18,14 +18,13 @@ module CurryBuilder (buildCurry, smake) where
import Control.Monad (liftM) import Control.Monad (liftM)
import Data.Maybe (catMaybes, mapMaybe) import Data.Maybe (catMaybes, mapMaybe)
import System.FilePath (normalise) import System.FilePath (normalise)
import Text.PrettyPrint
import Curry.Base.Ident import Curry.Base.Ident
import Curry.Base.Pretty
import Curry.Files.Filenames import Curry.Files.Filenames
import Curry.Files.PathUtils import Curry.Files.PathUtils
import Base.Messages import Base.Messages
(info, status, Message, message, abortWithMessage, abortWithMessages)
import CompilerOpts (Options (..), TargetType (..)) import CompilerOpts (Options (..), TargetType (..))
import CurryDeps (Source (..), flatDeps) import CurryDeps (Source (..), flatDeps)
...@@ -33,38 +32,33 @@ import Modules (compileModule) ...@@ -33,38 +32,33 @@ import Modules (compileModule)
-- |Compile the Curry module in the given source file including all imported -- |Compile the Curry module in the given source file including all imported
-- modules, depending on the 'Options'. -- modules, depending on the 'Options'.
buildCurry :: Options -> String -> IO () buildCurry :: Options -> String -> CYIO ()
buildCurry opts s = do buildCurry opts s = do
target <- findCurry opts s fn <- findCurry opts s
case target of srcs <- flatDeps opts fn
Left err -> abortWithMessage err makeCurry (defaultToFlatCurry opts) srcs fn
Right fn -> do where
(srcs, depErrs) <- flatDeps opts fn defaultToFlatCurry opt
if not $ null depErrs | null $ optTargetTypes opt = opt { optTargetTypes = [FlatCurry] }
then abortWithMessages depErrs | otherwise = opt
else makeCurry (defaultToFlatCurry opts) srcs fn
where
defaultToFlatCurry opt
| null $ optTargetTypes opt = opt { optTargetTypes = [FlatCurry] }
| otherwise = opt
-- |Search for a compilation target identified by the given 'String'. -- |Search for a compilation target identified by the given 'String'.
findCurry :: Options -> String -> IO (Either Message FilePath) findCurry :: Options -> String -> CYIO FilePath
findCurry opts s = do findCurry opts s = do
mbTarget <- findFile `orIfNotFound` findModule mbTarget <- findFile `orIfNotFound` findModule
case mbTarget of case mbTarget of
Nothing -> return $ Left complaint Nothing -> left [complaint]
Just fn -> return $ Right fn Just fn -> right fn
where where
canBeFile = isCurryFilePath s canBeFile = isCurryFilePath s
canBeModule = isValidModuleName s canBeModule = isValidModuleName s
moduleFile = moduleNameToFile $ fromModuleName s moduleFile = moduleNameToFile $ fromModuleName s
paths = optImportPaths opts paths = optImportPaths opts
findFile = if canBeFile findFile = if canBeFile
then lookupCurryFile paths s then liftIO $ lookupCurryFile paths s
else return Nothing else return Nothing
findModule = if canBeModule findModule = if canBeModule
then lookupCurryFile paths moduleFile then liftIO $ lookupCurryFile paths moduleFile
else return Nothing else return Nothing
complaint complaint
| canBeFile && canBeModule = errMissing "target" s | canBeFile && canBeModule = errMissing "target" s
...@@ -78,9 +72,10 @@ findCurry opts s = do ...@@ -78,9 +72,10 @@ findCurry opts s = do
justFn -> return justFn justFn -> return justFn
-- |Compiles the given source modules, which must be in topological order -- |Compiles the given source modules, which must be in topological order
makeCurry :: Options -> [(ModuleIdent, Source)] -> FilePath -> IO () makeCurry :: Options -> [(ModuleIdent, Source)] -> FilePath -> CYIO ()
makeCurry opts srcs targetFile = mapM_ (process . snd) srcs makeCurry opts srcs targetFile = mapM_ (process . snd) srcs
where where
process :: Source -> CYIO ()
process (Source fn deps) = do process (Source fn deps) = do
let isFinalFile = dropExtension targetFile == dropExtension fn let isFinalFile = dropExtension targetFile == dropExtension fn
isEnforced = optForce opts || (not $ null $ optDumps opts) isEnforced = optForce opts || (not $ null $ optDumps opts)
...@@ -91,10 +86,10 @@ makeCurry opts srcs targetFile = mapM_ (process . snd) srcs ...@@ -91,10 +86,10 @@ makeCurry opts srcs targetFile = mapM_ (process . snd) srcs
actOutdated = if isFinalFile then compileFinal else compile actOutdated = if isFinalFile then compileFinal else compile
actUpToDate = if isFinalFile then skipFinal else skip actUpToDate = if isFinalFile then skipFinal else skip
interfaceExists <- doesModuleExist $ flatIntName fn interfaceExists <- liftIO $ doesModuleExist $ flatIntName fn
if interfaceExists && not (isEnforced && isFinalFile) if interfaceExists && not (isEnforced && isFinalFile)
then smake destFiles depFiles (actOutdated fn) (actUpToDate fn) then smake destFiles depFiles (actOutdated fn) (actUpToDate fn)
else (actOutdated fn) else actOutdated fn
process _ = return () process _ = return ()
compileFinal f = do compileFinal f = do
...@@ -131,12 +126,12 @@ makeCurry opts srcs targetFile = mapM_ (process . snd) srcs ...@@ -131,12 +126,12 @@ makeCurry opts srcs targetFile = mapM_ (process . snd) srcs
-- |A simple make function -- |A simple make function
smake :: [FilePath] -- ^ destination files smake :: [FilePath] -- ^ destination files
-> [FilePath] -- ^ dependency files -> [FilePath] -- ^ dependency files
-> IO a -- ^ action to perform if depedency files are newer -> CYIO a -- ^ action to perform if depedency files are newer
-> IO a -- ^ action to perform if destination files are newer -> CYIO a -- ^ action to perform if destination files are newer
-> IO a -> CYIO a
smake dests deps actOutdated actUpToDate = do smake dests deps actOutdated actUpToDate = do
destTimes <- catMaybes `liftM` mapM getModuleModTime dests destTimes <- catMaybes `liftM` mapM (liftIO . getModuleModTime) dests
depTimes <- mapM (abortOnMissing getModuleModTime) deps depTimes <- mapM (cancelMissing getModuleModTime) deps
make destTimes depTimes make destTimes depTimes
where where
make destTimes depTimes make destTimes depTimes
...@@ -146,10 +141,10 @@ smake dests deps actOutdated actUpToDate = do ...@@ -146,10 +141,10 @@ smake dests deps actOutdated actUpToDate = do
outOfDate tgtimes dptimes = or [ tg < dp | tg <- tgtimes, dp <- dptimes] outOfDate tgtimes dptimes = or [ tg < dp | tg <- tgtimes, dp <- dptimes]
abortOnMissing :: (FilePath -> IO (Maybe a)) -> FilePath -> IO a cancelMissing :: (FilePath -> IO (Maybe a)) -> FilePath -> CYIO a
abortOnMissing act f = act f >>= \res -> case res of cancelMissing act f = liftIO (act f) >>= \res -> case res of
Nothing -> abortWithMessage $ errModificationTime f Nothing -> left [errModificationTime f]
Just val -> return val Just val -> right val
errMissing :: String -> String -> Message errMissing :: String -> String -> Message
errMissing what which = message $ sep $ map text errMissing what which = message $ sep $ map text
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
Copyright : (c) 2002 - 2004 Wolfgang Lux Copyright : (c) 2002 - 2004 Wolfgang Lux
2005 Martin Engelke 2005 Martin Engelke
2007 Sebastian Fischer 2007 Sebastian Fischer
2011 - 2012 Björn Peemöller 2011 - 2013 Björn Peemöller
License : OtherLicense License : OtherLicense
Maintainer : bjp@informatik.uni-kiel.de Maintainer : bjp@informatik.uni-kiel.de
...@@ -16,26 +16,22 @@ ...@@ -16,26 +16,22 @@
dependencies and to update programs composed of multiple modules. dependencies and to update programs composed of multiple modules.
-} -}
-- TODO (bjp): Propagate errors
-- Currently errors during the dependency search (like missing files
-- or errors during parsing a module header) lead to calls of the error
-- function. This dramatically limits the usability as a library.
module CurryDeps module CurryDeps
( Source (..), flatDeps, deps, flattenDeps, sourceDeps, moduleDeps ) where ( Source (..), flatDeps, deps, flattenDeps, sourceDeps, moduleDeps ) where
import Control.Monad (foldM, liftM, unless) import Control.Monad (foldM)
import Data.List (isSuffixOf, nub) import Data.List (isSuffixOf, nub)
import qualified Data.Map as Map (Map, empty, insert, lookup, toList) import qualified Data.Map as Map (Map, empty, insert, lookup, toList)
import Text.PrettyPrint
import Curry.Base.Ident import Curry.Base.Ident
import Curry.Base.Message (runMsg, Message, message) import Curry.Base.Message (runMsg)
import Curry.Base.Pretty
import Curry.Files.Filenames import Curry.Files.Filenames
import Curry.Files.PathUtils import Curry.Files.PathUtils
import Curry.Syntax (Module (..), ImportDecl (..), parseHeader, patchModuleId) import Curry.Syntax
(Module (..), ImportDecl (..), parseHeader, patchModuleId)
import Base.Messages (abortWithMessage, internalError) import Base.Messages
import Base.SCC (scc) import Base.SCC (scc)
import CompilerOpts (Options (..), Extension (..)) import CompilerOpts (Options (..), Extension (..))
...@@ -50,11 +46,15 @@ type SourceEnv = Map.Map ModuleIdent Source ...@@ -50,11 +46,15 @@ type SourceEnv = Map.Map ModuleIdent Source
-- |Retrieve the dependencies of a source file in topological order -- |Retrieve the dependencies of a source file in topological order
-- and possible errors during flattering -- and possible errors during flattering
flatDeps :: Options -> FilePath -> IO ([(ModuleIdent, Source)], [Message]) flatDeps :: Options -> FilePath -> CYIO [(ModuleIdent, Source)]
flatDeps opts fn = flattenDeps `liftM` deps opts Map.empty fn flatDeps opts fn = do
sEnv <- deps opts Map.empty fn
case flattenDeps sEnv of
(env, [] ) -> right env
(_ , errs) -> left errs