Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: flag: add Func #39557

Closed
earthboundkid opened this issue Jun 12, 2020 · 13 comments
Closed

proposal: flag: add Func #39557

earthboundkid opened this issue Jun 12, 2020 · 13 comments

Comments

@earthboundkid
Copy link
Contributor

I wrote a library of extensions to the flag package. Most of them are too specific to fit into flag itself or easy enough to write on one's own that there's no real point in adding them to the standard library, but there is one extension that I think would make a good addition to the standard library, flagext.Callback.

flagext.Callback is essentially just a wrapper that converts a function to a flag.Value. flag.Value has two methods (plus an optional third method for flag.Getter), but the String() string (and Get() interface{}) method is pure boilerplate and can be ignored by just putting a Stringer onto a bare func(s string) error.

My function's signature is

// Callback is a convenience function for defining a Set(string) error function
// on a flag.FlagSet without constructing a new type.
//
// If nil, fl defaults to flag.CommandLine. Value is only used for showing the
// default in usage help.
func Callback(fl *flag.FlagSet, name, value, usage string, cb func(string) error)

Example:

mode := "none"
flagext.Callback(fs, "mode", mode, "what mode to use", func(s string) error {
    if s != strings.ToLower(s) {
        return fmt.Errorf("mode must be lower case")
    }
    mode = s
    return nil
})

I think that this, like http.HandlerFunc, is so convenient that it would be good to have in the standard library. If it were in the standard library, it could be a method on flagset instead of a function that takes a flagset, so adding it to the standard library would have advantages that couldn't be reproduced by just importing my external package.

My proposed addition is

func (f *FlagSet) Callback(name, value, usage string, cb func(string) error)

and the corresponding top level package function.

@gopherbot gopherbot added this to the Proposal milestone Jun 12, 2020
@ianlancetaylor ianlancetaylor added this to Incoming in Proposals (old) Jun 12, 2020
@rsc rsc changed the title proposal: flag: Add flag.CallBack proposal: flag: add CallBack Jun 17, 2020
@rsc
Copy link
Contributor

rsc commented Jun 17, 2020

In the compiler we have this badly-named function:

func Flagfn1(name, usage string, f func(string)) {
	flag.Var(fn1(f), name, usage)
}

type fn1 func(string)

func (f fn1) Set(s string) error {
	f(s)
	return nil
}

func (f fn1) String() string { return "" }

A better name would be flag.Func probably.

One downside of this is that returning the empty string from String means that flag.PrintDefaults never prints a default for that setting. Maybe that's OK.

It's unclear that the value argument is necessary in this case. Maybe the API (at the top level, same in FlagSet) would be:

func Func(name, usage string, f func(string))

Thoughts?

/cc @robpike

@rsc rsc changed the title proposal: flag: add CallBack proposal: flag: add Func Jun 17, 2020
@rsc rsc moved this from Incoming to Active in Proposals (old) Jun 17, 2020
@earthboundkid
Copy link
Contributor Author

In my flagext package, I set the default in Usage with value but have to explain in the docs that it doesn't do anything besides change the help text. It might be simpler to just drop it and let users write (default ...) by hand in their usage string.

@earthboundkid
Copy link
Contributor Author

I think it should be func(string) error, not just func(string). Returning an error lets you complain if the value is malformed or unusable.

@earthboundkid
Copy link
Contributor Author

Sorry for the rapid fire responses.

If the signature were func Func(name, usage string, f func(string) error) and you wanted to just use func(string), it's very easy to add an inline wrapper that returns nil, but vice-versa, it's not clear what to do with an error if you have func(string) error and need func(string).

@rsc
Copy link
Contributor

rsc commented Jun 24, 2020

Yes, returning an error from the func makes a lot of sense.
(In the compiler I bet the func just calls fatal, but that's not right in general.)

OK, so it sounds like the API is:

func Func(name, usage string, f func(string) error)

Any other thoughts?

@earthboundkid
Copy link
Contributor Author

Seems good to me. :-) What should the example in the docs do that would be wryly funny?

earthboundkid added a commit to earthboundkid/go that referenced this issue Jun 26, 2020
earthboundkid added a commit to earthboundkid/go that referenced this issue Jun 26, 2020
@gopherbot
Copy link

Change https://golang.org/cl/240014 mentions this issue: flag: add Func

@robpike
Copy link
Contributor

robpike commented Jun 27, 2020

There's a CL but the proposal is still open. I had to read the issue and the CL code several times to understand what this is and why we want it. It's not a bad idea, just badly explained.

@earthboundkid
Copy link
Contributor Author

earthboundkid commented Jun 27, 2020

Yes, the code part of the CL is trivial, so I just sent it off during a down period today. The hard part will be writing a good docstring.

earthboundkid added a commit to earthboundkid/go that referenced this issue Jun 29, 2020
@rsc
Copy link
Contributor

rsc commented Jul 8, 2020

Ignoring the CL, it sounds like people are generally in favor of the API discussed above.

Based on the discussion, then, this seems like a likely accept.

@rsc rsc moved this from Active to Likely Accept in Proposals (old) Jul 8, 2020
@rsc
Copy link
Contributor

rsc commented Jul 15, 2020

We can work out the docs separately. No change in consensus, so accepted.

@rsc rsc moved this from Likely Accept to Accepted in Proposals (old) Jul 15, 2020
@rsc rsc modified the milestones: Proposal, Backlog Jul 16, 2020
@rsc rsc changed the title proposal: flag: add Func flag: add Func Jul 16, 2020
@earthboundkid
Copy link
Contributor Author

Now that Go 1.15 is out, can the CL be reviewed or do I need to do something else first?

@dolmen
Copy link
Contributor

dolmen commented Jan 28, 2021

For those stuck with Go below 1.16, you can already use the type Func from package github.com/dolmen-go/flagx with flag.Var.

@golang golang locked and limited conversation to collaborators Jan 28, 2022
@dmitshur dmitshur modified the milestones: Backlog, Go1.16 Feb 15, 2022
@seankhliao seankhliao changed the title flag: add Func proposal: flag: add Func Jul 8, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
No open projects
Development

Successfully merging a pull request may close this issue.

6 participants