1
\$\begingroup\$

Here's is the code I'd like reviewed:

module Main where
import Control.Monad ( when )
import System.Exit ( exitSuccess )
import Idris.AbsSyntax
import Idris.Error
import Idris.CmdOptions
import Idris.Info
import Idris.Info.Show
import Idris.Package
import Idris.Main
import Util.System ( setupBundledCC )
processShowOptions :: [Opt] -> Idris ()
processShowOptions opts = runIO $ do
 when (ShowAll `elem` opts) $ showExitIdrisInfo
 when (ShowLoggingCats `elem` opts) $ showExitIdrisLoggingCategories
 when (ShowIncs `elem` opts) $ showExitIdrisFlagsInc
 when (ShowLibs `elem` opts) $ showExitIdrisFlagsLibs
 when (ShowLibdir `elem` opts) $ showExitIdrisLibDir
 when (ShowPkgs `elem` opts) $ showExitIdrisInstalledPackages
check :: [Opt] -> (Opt -> Maybe a) -> ([a] -> Idris ()) -> Idris ()
check opts extractOpts action = do
 case opt extractOpts opts of
 [] -> return ()
 fs -> do action fs
 runIO exitSuccess
processClientOptions :: [Opt] -> Idris ()
processClientOptions opts = check opts getClient $ \fs -> case fs of
 (c:_) -> do
 setVerbose False
 setQuiet True
 case getPort opts of
 Just DontListen -> ifail "\"--client\" and \"--port none\" are incompatible"
 Just (ListenPort port) -> runIO $ runClient (Just port) c
 Nothing -> runIO $ runClient Nothing c
processPackageOptions :: [Opt] -> Idris ()
processPackageOptions opts = do
 check opts getPkgCheck $ \fs -> runIO $ do
 mapM_ (checkPkg opts (WarnOnly `elem` opts) True) fs
 check opts getPkgClean $ \fs -> runIO $ do
 mapM_ (cleanPkg opts) fs
 check opts getPkgMkDoc $ \fs -> runIO $ do
 mapM_ (documentPkg opts) fs
 check opts getPkgTest $ \fs -> runIO $ do
 mapM_ (testPkg opts) fs
 check opts getPkg $ \fs -> runIO $ do
 mapM_ (buildPkg opts (WarnOnly `elem` opts)) fs
 check opts getPkgREPL $ \fs -> case fs of
 [f] -> replPkg opts f
 _ -> ifail "Too many packages"
-- | The main function for the Idris executable.
runIdris :: [Opt] -> Idris ()
runIdris opts = do
 runIO setupBundledCC
 processShowOptions opts -- Show information then quit.
 processClientOptions opts -- Be a client to a REPL server.
 processPackageOptions opts -- Work with Idris packages.
 idrisMain opts -- Launch REPL or compile mode.
-- Main program reads command line options, parses the main program, and gets
-- on with the REPL.
main :: IO ()
main = do
 opts <- runArgParser
 runMain (runIdris opts)

I'd like to improve it. There are two main related problems.

This code uses exitSuccess for early exit. This then leads to a misleading piece of code in runIdris.

I'd prefer runIdris to look something like:

runIdris opts = do
 runIO setupBundledCC
 runIO execute (processShowOptions opts
 <|> processClientOptions opts
 <|> processPackageOptions opts
 <|> idrisMain opts)

I think I've worked out a way forward but would love to hear your thoughts.

asked Sep 15, 2016 at 4:05
\$\endgroup\$
4
  • \$\begingroup\$ You can do case args of "one" : args -> command1 args; "two" : args -> command2 args; _ -> usage. \$\endgroup\$ Commented Sep 16, 2016 at 21:05
  • \$\begingroup\$ Unfortunately I cannot do that in the real code. I worked out that I can wrap the IO action in a Maybe and use <|>. Additionally, I can wrap the Maybe in a First and use Monoid mconcat. \$\endgroup\$ Commented Sep 16, 2016 at 22:05
  • 3
    \$\begingroup\$ It really helps to have at least a worded summary of what the code is doing before jumping straight into a code wall. Please consider adding something to ease the reading experience. \$\endgroup\$ Commented Sep 26, 2016 at 16:43
  • 1
    \$\begingroup\$ What is Opt type? \$\endgroup\$ Commented Oct 21, 2016 at 12:39

1 Answer 1

2
\$\begingroup\$

This looks fine. There is some replication in processPackageOptions, but that's manageable. Your syntactical variant isn't possible, though, since Idris isn't an instance of Alternative. type Idris = StateT IState (ExceptT Err IO) would only be an instance of Alternative if Err was an instance of Monoid, which it isn't.

You could wrap Idris in another short-circuiting monad, for example Either ExitCode (Idris a) or a transformer variant, but you have to replace all runIO with liftIdrisIO or similar. That might be too much.

But I concur, the exitSuccess in check isn't that obvious and should be made more obvious at type level.

answered Apr 9, 2018 at 12:19
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.