United Kingdom: +44 (0)208 088 8978

A quick look at Argu

Argu helps you write user-friendly command line applications. In this blogpost, we showcase the most basic use-case.

We're hiring Software Developers

Click here to find out more

Want to write a commmand line application with a lot of command line options? Argu helps you do so by allowing you to model command line arguments as Discriminated unions.

By default, command line options are just a simple list of strings. Argu converts this list to typed values you can address using union types. Let's see how that works!

Set up a new F# console application, installing Argu

mkdir argu-helloWorld
cd argu-helloWorld
dotnet new console -lang "f#"
dotnet add package Argu

Now you are ready to write your app in Program.fs.
Let's start off with a basic Hello World.

[<EntryPoint>]
let main(args) =
    printfn "Hello World!"

    0

Let's create an option to greet a specific person instead of "World".
We define a type CliArguments, with a Name case. We also implement the IArgnParserTemplate interface, so Argu can display descriptions of our arguments.

open Argu

type CliArguments =
    | Name of name:string

    interface IArgParserTemplate with

        member s.Usage =
            match s with
            | Name _ -> "The name of the entity to be greeted." 

Now we can create a parser in our application. By Default, Argu throws an exception when creating a parseError, but they provide a ProcessExiter you can pass to the parser to gracefully exit and display an appropriate message.
We can then parse the args passed to our main function into the parser, so we can use the result to get our values.

[<EntryPoint>]
let main (args) =
    let errorHandler = ProcessExiter(None)
    let parser =
        ArgumentParser.Create<CliArguments>(programName = "helloWorld.exe", errorHandler = errorHandler)

    let result = parser.ParseCommandLine(args)

    let name = result.TryGetResult Name |> Option.defaultValue "World"

    printfn $"Hello, {name}!"
    0

try running the application with the name argument:

PS C:\Users\foo\argu-helloWorld> dotnet run --name Benno
Hello, Benno!

Also try running it with a non-existing argument:

PS C:\Users\foo\helloWorld> dotnet run --foo bar
ERROR: unrecognized argument: '--foo'.
USAGE: helloWorld.exe [--help] [--name <name>]

OPTIONS:

    --name <name>         The name of the entity to be greeted.
    --help                display this list of options.

You can also run postprocessing using Argu, for example to validate data. This means if parsing fails, the user is presented with an argu-generated error page.

let's add a parseName function, that checks if it only contains letters.

...
let parseName name =
    let isAlphabetical =
        name |> Seq.forall (fun character -> System.Char.IsLetter(character))

    match isAlphabetical with
    | true -> name
    | false -> failwith "Name can only contain letters."

[<EntryPoint>]
let main (args) =
    ...
    let name =
        result.TryPostProcessResult(<@ Name @>, parseName)
        |> Option.defaultValue "World"

    printfn $"Hello, {name}!"
    0

Argu comes with a bunch of customizations for arguments. Let's make the name argument mandatory:

type CliArguments =
    | [<Mandatory>] Name of name:string

If we try the application without passing --name, we get an error:

PS C:\Users\foo\helloWorld> dotnet run
ERROR: missing parameter '--name'.
USAGE: helloWorld.exe [--help] --name <name>

OPTIONS:

    --name <name>         The name of the entity to be greeted.
    --help                display this list of options.

These are just the most basic functionalities of Argu. It also allows you to implement Subcommands, and it can unparse too! As you can see, Agru is a safe an easy way to handle your command line parameters. It's also used by essential Fsharp tooling such as FAKE and Paket. Give it a try in your next CLI application.

Thanks to Eirik Tsarpalis and all other contributors for providing us with this awesome tool!