haskell library to parse commandline args - an alternative to optparse-applicative
 
 
 
Go to file
Lennart Spitzner 6a45f4b3a6 Add withReorder function 2017-05-16 13:28:32 +02:00
src/UI/Butcher Add withReorder function 2017-05-16 13:28:32 +02:00
src-tests Implement mapOut, peekInput, cmd child merging 2017-05-16 12:22:28 +02:00
srcinc Implement mapOut, peekInput, cmd child merging 2017-05-16 12:22:28 +02:00
.gitignore initial commit 2016-06-08 12:42:22 +02:00
ChangeLog.md initial commit 2016-06-08 12:42:22 +02:00
LICENSE initial commit 2016-06-08 12:42:22 +02:00
README.md Refactor module structure; Add haddock; Update README 2017-01-01 14:46:53 +01:00
Setup.hs initial commit 2016-06-08 12:42:22 +02:00
butcher.cabal Implement mapOut, peekInput, cmd child merging 2017-05-16 12:22:28 +02:00
example1.md Refactor module structure; Add haddock; Update README 2017-01-01 14:46:53 +01:00
example2.md Refactor module structure; Add haddock; Update README 2017-01-01 14:46:53 +01:00
example3.md Refactor module structure; Add haddock; Update README 2017-01-01 14:46:53 +01:00
iridium.yaml Silence some iridium checks 2016-10-11 00:24:08 +02:00

README.md

butcher

Chops a command or program invocation into digestable pieces.

Similar to the optparse-applicative package, but less features, more flexibility and more evil.

The main differences are:

  • Provides a pure interface by default

  • Exposes an evil monadic interface, which allows for much nicer binding of command part results to some variable name.

    In optparse-applicative you easily lose track of what field you are modifying after the 5th <*> (admittedly, i think -XRecordWildCards improves on that issue already.)

    Evil, because you are not allowed to use the monad's full power in this case, i.e. there is a constraint that is not statically enforced. See below.

  • The monadic interface allows much clearer definitions of commandparses with (nested) subcommands. No pesky sum-types are necessary.

Examples

The minimal example is

main = mainFromCmdParser $ addCmdImpl $ putStrLn "Hello, World!"

But lets look at a more feature-complete example:

main = mainFromCmdParserWithHelpDesc $ \helpDesc -> do

  addCmdSynopsis "a simple butcher example program"
  addCmdHelpStr "a very long help document"

  addCmd "version" $ do
    porcelain <- addSimpleBoolFlag "" ["porcelain"]
      (flagHelpStr "print nothing but the numeric version")
    addCmdHelpStr "prints the version of this program"
    addCmdImpl $ putStrLn $ if porcelain
      then "0.0.0.999"
      else "example, version 0.0.0.999"

  addCmd "help" $ addCmdImpl $ print $ ppHelpShallow helpDesc

  short <- addSimpleBoolFlag "" ["short"]
    (flagHelpStr "make the greeting short")
  name <- addStringParam "NAME"
    (paramHelpStr "your name, so you can be greeted properly")

  addCmdImpl $ do
    if short
      then putStrLn $ "hi, " ++ name ++ "!"
      else putStrLn $ "hello, " ++ name ++ ", welcome from butcher!"

Further:

The evil monadic interface

As long as you only use Applicative or (Kleisli) Arrow, you can use the interface freely. When you use Monad, there is one rule: Whenever you read any command-parts like in

f <- addFlag ...
p <- addParam ...

you are only allowed to use bindings bound thusly in any command's implemenation, i.e. inside the parameter to addCmdImpl. You are not allowed to force/inspect/patternmatch on them before that. good usage is:

addCmdImpl $ do
  print x
  print y

while bad would be

f <- addFlag
when f $ do
  p <- addParam
  -- evil: the existence of the param `p`
  -- depends on parse result for the flag `f`.

That means that checking if a combination of flags is allowed must be done after parsing. (But different commands and their subcommands (can) have separate sets of flags.)

(abstract) Package intentions

Consider a commandline invocation like "ghc -O -i src -Main.hs -o Main". This package provides a way for the programmer to simultaneously define the semantics of your program based on its arguments and retrieve documentation for the user. More specifically, i had three goals in mind:

  1. Straight-forward description of (sub)command and flag-specific behaviour
  2. Extract understandable usage/help commandline documents/texts from that descriptions, think of ghc --help or stack init --help.
  3. Extract necessary information to compute commandline completion results from any partial input. (This is not implemented to any serious degree.)

Semantics

(Sorry, this description is severely lacking, I know.)

Basic elements of a command are flags, parameters and subcommands. These can be composed in certain ways, i.e. flags can have a (or possibly multiple?) parameters; parameters can be grouped into sequences, and commands can have subcommands.

Commands are essentially String -> Either ParseError out where out can be chosen by the user. It could for example be IO ().

To allow more flexible composition, the parts of a command have the "classic" parser's type: String -> Maybe (p, String) where p depends on the part. Parse a prefix of the input and return something and the remaining input, or fail with Nothing.

A command-parser contains a sequence of parts and then a number of subcommands and/or some implementation.