Removing main side effects from public interface (Parser type) #59

Closed
gsscoder opened this Issue Feb 27, 2013 · 7 comments

Projects

None yet

2 participants

@gsscoder
Owner

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.

  • If parsing succeed, the instance is loaded with parsed values.
  • If parsing fails and ParserStateAttribute is used, the generic errors list will be available as always.

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.

PS:
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.

@nemec
Collaborator
nemec commented Feb 27, 2013

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.

@gsscoder
Owner

@nemec,
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/v1.9.71.2

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.

@gsscoder gsscoder added a commit that referenced this issue Feb 28, 2013
@gsscoder Merge branch 'issue59-sideeff' into develop-1.9.8-beta -> issue #59.
Conflicts:
	CommandLine.sln.DotSettings.user
	README.md
	src/SharedAssemblyInfo.cs
26d549f
@gsscoder
Owner

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.

Contributors: #62.

A nice day to everyone! :))

@gsscoder gsscoder closed this Feb 28, 2013
@gsscoder
Owner

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?

@gsscoder
Owner
gsscoder commented Mar 2, 2013

@nemec,
rembember we talked about that where T : new() constrain the parser to use creatable types...?

We can do as most IoC does, providing an overload like

T ParseArguments<T>(Func<T> builder, string[] args, Action onFail)

so that

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...

@nemec
Collaborator
nemec commented Mar 3, 2013

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`
@gsscoder
Owner
gsscoder commented Mar 3, 2013

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment