vendredi 10 septembre 2021

Command handler receives wrong arguments when it's a let binding but works as lambda

I'm trying to use F# and System.CommandLine to make a little CLI tool. A command can have a handler callback that is called when the command is used. Usually one can define several flags for a command and the name of the flag is used to bind to an argument of the handler function with the same name.

Example

myApp foo --a

will call the handler for the foo command

let handler (a: bool) (b: bool) = // a should be true, b should be false
   ...

However this doesn't work and both a and b are false when I bind the handler that is a let binding:

let fooCommand = Command ...
fooCommand.Handler <- CommandHandler.Create handler
// will call handler false false

But, when I use a lambda directly it works fine and the function arguments have the correct values

let fooCommand = Command ...
fooCommand.Handler <- CommandHandler.Create (fun (a: bool) (b: bool) -> handler a b)
// will call handler true false

Why is that? Why does the handler work as a lambda but not as a let binding?

Here is a MRE

#r "nuget: System.CommandLine, 2.0.0-beta1.21308.1"

open System.CommandLine
open System.CommandLine.Invocation
open System.CommandLine.Parsing

let opts = [Option<bool>([|"--a"|]); Option<bool>([|"--b"|])]

let fooCmd = Command("foo", "")
List.iter fooCmd.AddOption opts

let barCmd = Command("bar", "")
List.iter barCmd.AddOption opts

let handler (a: bool) (b: bool) = 
    printfn "%A" {| a = a; b = b|}

fooCmd.Handler <- CommandHandler.Create handler

barCmd.Handler <- CommandHandler.Create (fun (a: bool) (b: bool) -> handler a b)

let root = RootCommand("")
root.Add fooCmd
root.Add barCmd

printfn "foo --a: "
root.Invoke("foo --a")

printfn "bar --a: "
root.Invoke("bar --a")

which prints

foo --a: 
{ a = false
  b = false }
bar --a: 
{ a = true
  b = false }




Aucun commentaire:

Enregistrer un commentaire