Argument parsing
Walk a list of command-line arguments into a config record, with proc reporting the first error.
module argumentParsing
type Config = (
help: bool
verbose: bool
input: string
)
type Arguments = list(string)
type ParseError =
| #helpRequested
| #missingArgument(string)
-- The value following a flag, found by walking the argument list two at a time
def requiredArgument: (Arguments, string) -> #some(string) | #error(#missingArgument(string))
= { args, name ->
rec loop = {
| [] | [_] -> #error(#missingArgument(name))
| [flag, value, ...rest] ->
if flag == name then #some(value) else loop([value, ...rest])
}
loop(args)
}
def optionalSwitch: (Arguments, string) -> bool = { args, name -> args \contains(name) }
def checkHelp: Arguments -> #some(()) | #error(#helpRequested) = { args ->
if optionalSwitch(args, "--help") then #error(#helpRequested) else #some(())
}
-- `proc` short-circuits on the first `#error`, so the happy path reads top to bottom
def parseArguments: Arguments -> #some(Config) | #error(ParseError) = { args -> proc {
try checkHelp(args)
def input = try requiredArgument(args, "--input")
return Config(
help = false
verbose = optionalSwitch(args, "--verbose")
input = input
)
}}
@entry
def main = {
def args = ["--input", "foo.txt", "--verbose"]
match parseArguments(args)
| #some(config) -> println("Processing input '{config.input}' verbose={config.verbose}")
| #error(#helpRequested) -> println("Usage: example [--help] [--verbose] [--input <input file>]")
| #error(#missingArgument(name)) -> println("Error: command line argument {name} is missing")
}
This program lives in the repository as examples/functional-argument-parsing.nyx.