As you seen with latest release a lot was done to simplify the library.
Don't get me wrong, I'll not change basic concepts of the API; but I'd like to embrace good design from functional world for make our library strongest.
When you declare the options type:
var options = new Options();
options is in state X (initialized) and after
var result = new Parser().ParseArguments(args, options);
options is in state Y (with parsed values)
So we can say that ParseArguments caused side effects. Another one, is the result (also influenced by the same function... opssss method!).
Incredibly the solution is quite easy:
T Parser.ParseArguments<T>(string args, Action onFail)
The method with this new signature constructs the instance and returns it.
But if parsing fails an appropriate delegate (Action onFail) is called.
Other advantages: convergence of standard methods with strict methods (@nemec suggested feature).
Functional paradigms proves their effectiveness from '70 (and before), please dig into the argument if not convinced. :))
As always, opinions (or flames) are welcome.
Some side effects for the moment will remain. E.g.: printing out the help. As said, I'm moving to a removing the most evident of these.
The downside to this approach is that the Options class must have a default constructor. This is probably a good idea anyway, but something to keep in mind.
yes this true. But will gain solidity and one instruction in less to get arguments parsed. :))
I'm doing some experiment on anther branch, for now it seems promising.
But if something unexpected from the first analysis arise, I'll not merge it to master.
Here the branch (uncomplete), https://github.com/gsscoder/commandline/tree/issue59-sideeff
I've also create a new wiki section, https://github.com/gsscoder/commandline/wiki/Latest-Beta
If the new beta will become "official" I'll explain "what changes", "why" and "why it could be better"
Anyway the latest stable is tagged and well defined, https://github.com/gsscoder/commandline/tree/v22.214.171.124
Thanks for the comments!
PS: I've explained in detail all these things for future reference / other users, I know you're more than familiar with what I quoted.
Merge branch 'issue59-sideeff' into develop-1.9.8-beta -> issue #59.
Work is moved to new official beta https://github.com/gsscoder/commandline/tree/develop-1.9.8-beta branch.
Some was done, other things must be done, but I think that this could be a nice starting point.
The issue could be considered close (also if other msg maybe appended here).
When some internal refactoring goes to end -> it will be merged to master.
A nice day to everyone! :))
I want to add. The public API of CommandLine.Parser could be considered in its final form.
T ParseArguments<T>(string args, Action onFail) where T : new()
T ParseArguments<T>(string args, Action<string, object> onVerbCommand, Action onFail) where T : new()
A lot cleaner now... or not?
rembember we talked about that where T : new() constrain the parser to use creatable types...?
where T : new()
We can do as most IoC does, providing an overload like
T ParseArguments<T>(Func<T> builder, string args, Action onFail)
var parser = new Parser();
var options = parser.ParseArguments<AbstractOptions>(() => new ConcreteOptions(), args, () => Environment.Exit(-1));
// we are here, options can't be null in any case! :)
What do you think about?
It avoid side effects on options instance and let devs create it...
That would work. Of course it's easy to introduce side effects, but that's the implementer's problem rather than yours ;)
var parser = new Parser();
var optInstance = new ConcteteOptions();
var options = parser.ParseArguments<AbstractOptions>(() => optInstance, args, () => Environment.Exit(-1));
// ReferenceEquals(optInstance, options) is `true`
@nemec, great! you've read my mind... This is exactly what I've thought about it.
Talking of side side effects (or intended effects as printing help) I think they are not an absolute evil.
The same F# List.map implementation use side effects internally (ref. Don Syme - Expert F# 3.0) but it doesn't expose its mutable structures to the outside world, hence we can say it's purely functional.
I'm not try to change this library to a purely functional piece a software (inside/out), but a stated in the post I'd like to remove all not benign side effects from public surface of the API.
Always toward usability and a not-verbose-syntax compromise.
PS: is also true, if you look to the new branch, that at a lot is done internally / and other can and will be done.