Command Line Subcommands in Haskell
Here’s the translation of the Go code to Haskell, along with explanations in Markdown format suitable for Hugo:
Our program demonstrates how to create command-line subcommands in Haskell. We’ll use the optparse-applicative
library, which provides a powerful and flexible way to parse command-line options.
First, let’s import the necessary modules:
import Options.Applicative
import Data.Semigroup ((<>))
import System.Environment (getArgs)
Now, let’s define our data types for the subcommands and their options:
data FooOptions = FooOptions
{ fooEnable :: Bool
, fooName :: String
}
data BarOptions = BarOptions
{ barLevel :: Int
}
data Command
= Foo FooOptions
| Bar BarOptions
Next, we’ll define the parsers for our subcommands:
fooOptions :: Parser FooOptions
fooOptions = FooOptions
<$> switch
( long "enable"
<> help "enable" )
<*> strOption
( long "name"
<> metavar "NAME"
<> help "name" )
barOptions :: Parser BarOptions
barOptions = BarOptions
<$> option auto
( long "level"
<> metavar "LEVEL"
<> help "level"
<> value 0 )
commandParser :: Parser Command
commandParser = subparser
( command "foo"
(info (Foo <$> fooOptions)
(progDesc "Foo subcommand"))
<> command "bar"
(info (Bar <$> barOptions)
(progDesc "Bar subcommand"))
)
Now, let’s define our main function:
main :: IO ()
main = do
cmd <- execParser opts
case cmd of
Foo (FooOptions enable name) -> do
putStrLn "subcommand 'foo'"
putStrLn $ " enable: " ++ show enable
putStrLn $ " name: " ++ name
Bar (BarOptions level) -> do
putStrLn "subcommand 'bar'"
putStrLn $ " level: " ++ show level
where
opts = info (commandParser <**> helper)
( fullDesc
<> progDesc "Program with subcommands"
<> header "command-line-subcommands - an example CLI program" )
To run the program, compile it with GHC:
$ ghc -o command-line-subcommands command-line-subcommands.hs
Now you can invoke the subcommands:
$ ./command-line-subcommands foo --enable --name=joe
subcommand 'foo'
enable: True
name: joe
$ ./command-line-subcommands bar --level 8
subcommand 'bar'
level: 8
If you try to use flags from one subcommand with another, you’ll get an error:
$ ./command-line-subcommands bar --enable
Invalid option `--enable'
Usage: command-line-subcommands bar [--level LEVEL]
Bar subcommand
Available options:
--level LEVEL level
-h,--help Show this help text
This Haskell implementation provides a type-safe and declarative way to define command-line subcommands. The optparse-applicative
library offers many more features for complex command-line interfaces, including support for positional arguments, which we haven’t demonstrated here.