-
Notifications
You must be signed in to change notification settings - Fork 26
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
Unpack macro #7
Comments
Rust does this in a different way: https://github.com/docopt/docopt.rs Seems like there are many different ways to get types out of it. From what I understood, either you create an object with some specific variable names for it to populate, or you specify the types in the call, or you use |
@ReneSac There are two ways. I don't know of anyone using the |
Thanks for the input. Reading again now, I think I understood. I still have some questions: How rust handles arguments The good thing about using a naming convention is the standardization and simplicity. The bad thing is the lack of flexibility compared to my proposal, and the fact it must be namespaced. Both things are kinda mutually exclusive. One thing I didn't like about returning a struct is that you need an additional docopt!() outside your main(), because it creates a new type. That is ugly and redundant. Fully defining the struct type must also be done at the top level, but it isn't ugly and the redundancy comes in form of explicitly listing all the elements, which is good for readability. |
It doesn't differentiate them. Rust identifiers cannot contain
I think this would actually translate to two different field members,
Sure. I'm just sharing. I have no opinions. :-)
I don't know what you're talking about. The #![feature(plugin)]
#![plugin(docopt_macros)]
extern crate docopt;
extern crate rustc_serialize;
docopt!(Args derive Debug, "Usage: prog <a> <b> <c>");
fn main() {
let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
println!("{:?}", args);
} Output:
|
Yeah, that is what I expected. Nim identifiers can't contain
Ok, just wanted to know if it was required per docopt specification/tests.
A non-redundant version in my eyes would be something like the following, that ideally would behave exactly like yours did.: #![feature(plugin)]
#![plugin(docopt_macros)]
extern crate docopt;
extern crate rustc_serialize;
fn main() {
let args = docopt!(Args derive Debug, "Usage: prog <a> <b> <c>");
println!("{:?}", args);
} But as it must also create a new type I imagine it won't work like that. |
Indeed not. And I'm not sure what redundancy you removed other than writing Anyway, good luck! Let me know if you have any other questions on how it works. |
A lot of awesome things could be done if I could run this at compile time. But there are a few reasons why it is impossible: VM can't do ref types and regex. ReneSac's proposal is something I haven't thought about. It is probably doable without compile-time information, but we would need to define conversions for every pair of source and destination types. I don't like the hardcoded conversions. Users can't choose their own type or conversion. And, finally, what if an argument can have different types depending on context? |
Why would we need ref types? Outputting a value type object or a tuple is plenty for this use case.
I didn't consider that there were that many source types. Well, you only need to support a few destination types by default, and you don't need to implement all possible combinations between them. Just give an appropriate compilation error if the user asks to convert from seq[string] to float, for example. The user will then have to output it as some supported type and handle it the way he wants latter. More combinations can be added to the program with time. But yeah, I'm not sure how the rust implementation deals with this combinatorial explosion problem.
Let the user define their own
Output it as a Value and handle that separately. Or redesign your command line interface. |
It uses the type based decoding infrastructure: https://github.com/docopt/docopt.rs/blob/master/src/dopt.rs#L695-L857 --- The user states the types they want and the decoder tries to convert them. If you have a custom type, then you have to teach it how to decode to your type. |
Sorry, misclicked on close. Bellow an update to my proposal syntax. The default type declaration syntax is sure nicer than the simple comma separated commandeer syntax. There is no need for an intermediate table, all can be done at compile time. The values of course will only be known at run time. Also, the rust prefix syntax can be used as a type of disambiguation. const doc = """
Naval Fate.
Usage:
naval_fate ship new <name>...
naval_fate ship <name> move <x> <y> [--speed=<kn>]
naval_fate ship shoot <x> <y>
naval_fate mine (set|remove) <x> <y> [--moored | --drifting]
naval_fate (-h | --help)
naval_fate --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting mine.
"""
import docopt, naval_fate_stuff
proc main() =
docopt(doc, version = "Naval Fate 2.0"):
drifting, moored, mine, cmd_move, cmd_new, remove, cmd_set, ship, shoot: bool
speed, x, y: float
name: seq[string]
if cmd_move and name in ships:
echo "Moving ship $# to ($#, $#) at $# kn".format(name, x, y, speed)
ships[name].move(x, y, speed=speed) By default it should require you to list all the variables with the type you want, except options that docopt would automatically exit uppon receiving like You may also notice I dropped disambiguation by "". But allowing optional rust prefixes creates new ambiguities, like if you have options named "x" and "flagx" in the same command line. One could simply disallow such clashes, allow only prefixed variables like rust, or reintroduce the "" disambiguation someway. An alternative version bellow showcasing the above paragraphs: import docopt
proc main() =
docopt(doc, version = "Naval Fate 2.0", autoFill=true):
mine_set:bool = "set"
m: bool = "--moored"
speed, x, y: float
name: seq[string]
|
I am not planning to do anything about this in the near future. I don't see any perfect solution. This doesn't have to be a part of the library (and maybe shouldn't seeing as it's a direct port). One can make whatever macro they want based on the returned table. |
Each port seems to be free to adapt to the syntax of the target language. The only requirement seems to be able to pass the test framework. I think docopt would be much more usable with this. But of course it is your call to work on this or not. And is your docopt already able to supply the table at compile time? Otherwise your last phrase is false. And a idea discussed in irc and not yet documented here: use dummy modules to provide convenient and robust namespacing between flags, cmds and args. Better than emulating namespacing via prefixes. |
The result of the docopt call seems a bit unwieldy to use. What about a macro to unpack the table into pretty and readly usable variables, kinda like commandeer?
It would be good if the table key can optionally be omitted if the variable name unambiguously refer to one of the table keys. From the front page example:
I think it is much better if the typing info is centralized like that, instead of scattered along the code with
parseFloat()
and such.The syntax is debatable. And it might be better to make an alternative docopt call to do that directly, w/o the need to create an args table. But of course, one should retain the old way for backwards compatibility/whoever likes it.
The text was updated successfully, but these errors were encountered: