Search code examples
f#command-line-interfacesystem.commandline

Is System.CommandLine F#-friendly?


For F# apps, I've traditionally used different functional-friendly command line parsers like Argu and CommandLineParser.

Now that Microsoft has come up with System.CommandLine (with potentially brings better support and documentation), is it usable from F#?


Solution

  • Functional-friendliness is subjective, but I found the API rather convenient for F#:

    open System.CommandLine
    open System.CommandLine.Invocation
    
    [<EntryPoint>]
    let main argv =
        let option1 = Option<string> "--option1"
        let option2 = Option<string> "--option2"
        let option3 = Option<string> "--option3"
        let option4 = Option<string> "--option4"
    
        let command1 = Command "command 1"
        command1.AddOption option1
        command1.AddOption option2
        command1.Handler <- CommandHandler.Create (fun option1 option2 -> Operation1.run option1 option2)
    
        let command2 = Command "command 2"
        command2.AddOption option3
        command2.AddOption option4
        command2.Handler <- CommandHandler.Create (fun option3 option4 -> Operation1.run option3 option4)
    
        let rootCommand = RootCommand "My tool."
        rootCommand.AddCommand command1
        rootCommand.AddCommand command2
    
        rootCommand.Invoke argv
    

    To make code more fun, some trivial helpers can help:

    open System.CommandLine
    
    let addCommand subCommand (command: Command) =
        command.AddCommand subCommand
        command
    
    let addOption option (command: Command) =
        command.AddOption option
        command
    
    let setHandler handler (command: Command) =
        command.Handler <- handler
        command
    

    To be used like:

    open System.CommandLine
    open System.CommandLine.Invocation
    
    [<EntryPoint>]
    let main argv =
        let option1 = Option<string> "--option1"
        let option2 = Option<string> "--option2"
        let option3 = Option<string> "--option3"
        let option4 = Option<string> "--option4"
    
        let command1 =
            Command "command 1"
            |> addOption option1
            |> addOption option2
            |> setHandler (CommandHandler.Create (fun option1 option2 -> Operation1.run option1 option2))
    
        let command2 =
            Command "command 2"
            |> addOption option3
            |> addOption option4
            |> setHandler (CommandHandler.Create (fun option3 option4 -> Operation2.run option3 option4))
    
        let rootCommand =
            RootCommand "My tool."
            |> addCommand command1
            |> addCommand command2
    
        rootCommand.Invoke argv
    
    

    This also seems to be a dedicated library for having System.CommandLine in the F# way.

    F# was likely taken into mind during the library design, although there are some caveats, e.g. this one for command handler binding.

    In general, the library is still in the preview so the API might substantially change in the future.