[m-rev.] for review / comment: structured errors for getopt
Julien Fischer
jfischer at opturion.com
Wed Mar 26 17:47:19 AEDT 2014
On Wed, 26 Mar 2014, Peter Wang wrote:
> On Tue, 25 Mar 2014 17:21:34 +1100 (EST), Julien Fischer <jfischer at opturion.com> wrote:
>>
>> For review / feedback.
>>
>> I'm not intending to push this to github in its current form; in requesting a review
>> I am mainly seeking feedback on the following things:
>>
>> (1) the overall proposal.
>>
>> (2) in particular, the names of the constructors of the option_error/1 type and
>> their documentation.
>>
>> Before pushing this change (in whatever form it ends up) a corresponding set of
>> changes will be made to library/getopt.m.
>
> Not really related, but I would like to see getopt.m and getopt_io.m
> share an implementation. Perhaps getopt_io can define a typeclass for
> handling file_special and getopt defines a dummy instance of that just,
> but I haven't looked at it in detail.
The original justification for the two modules (from the log message
from the original commit of getopt_io) was:
Since processing options may now involve accessing a file, the main
predicate process_options now needs a pair of I/O state arguments.
Since some places where one wants to process options do not have I/O
states, we want to retain the old interface of process_options as well.
Since it is not practical to parameterize a group of predicates to work
both in the presence and in the absence of I/O states, I added the code
in a new module. While most changes to option handling should be done to
both getopt.m and getopt_io.m, I expect such changes to be few and far
between, so I think overall the new module is the best solution.
I am inclined to agree with that last bit and suspect that any shared
implementation is probably going to be more trouble than it's worth.
>> diff --git a/library/getopt_io.m b/library/getopt_io.m
>> index 84ea522..69cac88 100644
>> --- a/library/getopt_io.m
>> +++ b/library/getopt_io.m
>> @@ -153,6 +153,27 @@
>> option_table(OptionType)::in, maybe_option_table(OptionType)::out,
>> set(OptionType)::out, io::di, io::uo) is det.
>>
>> +% Variants of the above that return structured errors.
>> +% These behave as the above versions except that any error values returned are
>> +% members of the option_error/1 type rather than strings.
>> +
>> +:- pred getopt_io.process_options_se(option_ops(OptionType)::in(option_ops),
>> + list(string)::in, list(string)::out, maybe_option_table_se(OptionType)::out,
>> + io::di, io::uo) is det.
>> +
>> +:- pred getopt_io.process_options_se(option_ops(OptionType)::in(option_ops),
>> + list(string)::in, list(string)::out, list(string)::out,
>> + maybe_option_table_se(OptionType)::out, io::di, io::uo) is det.
>
> Perhaps it should continue option processing after encountering an
> error, so that the program can report multiple errors in a single run?
> Then the output arguments could be:
>
> list(string)::out, output_table(OptionType)::out,
> list(option_error(OptionType))::out,
>
> so changing the arity and allowing you to drop the _se suffix ;)
Returning multiple errors occurred to me as well, although I restricted
this change to cover only what the module currently supports.
(Having proper contexts for errors in file_special arguments would be
nice as well.)
>> @@ -255,12 +276,78 @@
>> ; string(string)
>> ; maybe_string(maybe(string)).
>>
>> -:- type option_table(OptionType) == map(OptionType, option_data).
>> +:- type option_table(OptionType) == map(OptionType, option_data).
>>
>> :- type maybe_option_table(OptionType)
>> ---> ok(option_table(OptionType))
>> ; error(string).
>>
>> +:- type maybe_option_table_se(OptionType)
>> + ---> ok(option_table(OptionType))
>> + ; error(option_error(OptionType)).
>> +
>> +:- type option_error(OptionType)
>> + ---> unrecognized(string)
>> + % An option that is not recognized appeared on the command line.
>> + % The argument gives the option as it appeared on the command line.
>> +
>> + % The first two arguments of the remaining constructors of this type
>> + % identify the option enumeration value and the string that appeared on the
>> + % command line for that option respectively.
>> +
>> + ; unknown_type(OptionType, string)
>> + % No type has been specified for the option.
>> +
>> + ; requires_argument(OptionType, string)
>> + % The option requires an argument but it occurred on the command
>> + % line without one.
> ...
>
> I suggest factoring out the first two arguments.
>
> :- type option_error(OptionType)
> ---> unrecognized(string)
> ; option_error(OptionType, string, option_error_reason).
>
> :- type option_error_reason
> ---> unknown_type
> ; requires_argument
> ; does_not_allow_argument(string)
> ; ...
Ok.
>> @@ -425,8 +548,8 @@ getopt_io.process_arguments([Option | Args0], Args, OptionOps,
>> Args = Args0
>> )
>> ;
>> - ErrorMsg = "unrecognized option `-" ++ Option ++ "'",
>> - Result = error(ErrorMsg),
>> + Error = unrecognized(Option),
>> + Result = error(Error),
>> OptionArgs = OptionArgs0,
>> Args = Args0
>> )
>
> I kept forgetting to fix that bug.
To be honest, I'd never even noticed it!
Cheers,
Julien.
More information about the reviews
mailing list