Mercury-Language/mercury

getopt.m is impossible to use because det inference fails with "Uncaught Mercury exception"

ulidtko opened this issue · 4 comments

The simplest "hello getopt" code fails to compile:

:- module hello_getopt.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.

:- implementation.

:- import_module getopt_io.
:- import_module char,string.

main -->
    io.command_line_arguments(Argv),
    process_options(optDescr, Argv, _, ParsedOpts),
    io.print(ParsedOpts).

:- type myoption ---> optFoo; optBar.

:- func optDescr = option_ops(myoption).
optDescr = option_ops(shortOpt, longOpt, defaultOpt).

:- pred shortOpt(char::in, myoption::out).
shortOpt('F', optFoo).
shortOpt('B', optBar).

:- pred longOpt(string::in, myoption::out).
longOpt("foo", optFoo).
longOpt("bar", optBar).

:- pred defaultOpt(myoption::in, option_data::out).
defaultOpt(optFoo, string("buzz")).
defaultOpt(optBar, int(42)).

Error message:

Uncaught Mercury exception:
Software Error: check_hlds.polymorphism: predicate `check_hlds.polymorphism.lambda_modes_and_det'/4: Sorry, not implemented: determinism inference for higher order predicate terms.

Specifying defaultOpt determinism explicitly does not help; neight does a separate :- mode defaultOpt declaration.

Since there's no way to use getopt/getopt_io APIs while not specifying any default values for options, the library is rendered basically useless.

The Mercury compiler uses getopt_io, and it works just fine. Your code would work too
if you declared the determinism of shortOpt, longOpt and defaultOpt.

@zsomogyi thanks!

Now I get this:

hello.m:013: In clause for `main(di, uo)':
hello.m:013:   mode error in conjunction. The next 2 error messages indicate
hello.m:013:   possible causes of this error.
hello.m:012:   In clause for `main(di, uo)':
hello.m:012:   in argument 1 of call to predicate
hello.m:012:   `getopt_io.process_options'/6:
hello.m:012:   mode error: variable `V_10' has instantiatedness `ground',
hello.m:012:   expected instantiatedness was
hello.m:012:   `bound(getopt_io.option_ops((pred((ground >> ground), (free >>
hello.m:012:   ground)) is semidet), (pred((ground >> ground), (free >>
hello.m:012:   ground)) is semidet), (pred((free >> ground), (free >> ground))
hello.m:012:   is nondet)) ; getopt_io.option_ops((pred((ground >> ground),
hello.m:012:   (free >> ground)) is semidet), (pred((ground >> ground), (free
hello.m:012:   >> ground)) is semidet), (pred((free >> ground), (free >>
hello.m:012:   ground)) is nondet), (pred((ground >> ground), (ground >>
hello.m:012:   ground), (ground >> ground), (free >> ground)) is semidet)) ;
hello.m:012:   getopt_io.option_ops_multi((pred((ground >> ground), (free >>
hello.m:012:   ground)) is semidet), (pred((ground >> ground), (free >>
hello.m:012:   ground)) is semidet), (pred((free >> ground), (free >> ground))
hello.m:012:   is multi)) ; getopt_io.option_ops_multi((pred((ground >>
hello.m:012:   ground), (free >> ground)) is semidet), (pred((ground >>
hello.m:012:   ground), (free >> ground)) is semidet), (pred((free >> ground),
hello.m:012:   (free >> ground)) is multi), (pred((ground >> ground), (ground
hello.m:012:   >> ground), (ground >> ground), (free >> ground)) is
hello.m:012:   semidet)))'.
hello.m:013:   In clause for `main(di, uo)':
hello.m:013:   in argument 2 of call to predicate `io.print'/3:
hello.m:013:   unique-mode error: the called procedure would clobber its
hello.m:013:   argument, but variable `STATE_VARIABLE_IO_11' is still live.

code:

:- module hello.

:- interface.
:- import_module io.

:- pred main(io::di, io::uo) is det.

:- implementation.

main -->
    io.command_line_arguments(Argv),
    process_options(optDescr, Argv, _, ParsedOpts),
    io.print(ParsedOpts).

:- import_module getopt_io.
:- import_module char,string.
:- import_module set,map.

:- type myoption ---> optFoo; optBar.

:- func optDescr = option_ops(myoption).
optDescr = option_ops(shortOpt, longOpt, defaultOpt).

:- pred shortOpt(char::in, myoption::out) is semidet.
shortOpt('F', optFoo).
shortOpt('B', optBar).

:- pred longOpt(string::in, myoption::out) is semidet.
longOpt("foo", optFoo).
longOpt("bar", optBar).

:- pred defaultOpt(myoption::in, option_data::out) is det.
defaultOpt(optFoo, string("buzz")).
defaultOpt(optBar, int(42)).

What am I doing wrong now?

You are not looking at other modules that use getopt and getopt_io correctly.
The Mercury distribution has several examples; deep_profiler/mdprof_cgi.m is a simple one.
Have a look, and you should find your problem in no time.

Compiling code for completeness:

:- module hello.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.

:- implementation.

main -->
    io.command_line_arguments(Args),
    %{ OptsDescr = optDescr },
    process_options(
        %OptsDescr,
        option_ops_multi(shortOpt, longOpt, defaultOpt),
        Args, _, ParsedOpts),
    io.print(ParsedOpts).

:- import_module getopt_io.
:- import_module char,string.
:- import_module set,map.

:- type myoption ---> optFoo; optBar.

%:- func optDescr = option_ops(myoption).
%optDescr = option_ops_multi(shortOpt, longOpt, defaultOpt).

:- pred shortOpt(char::in, myoption::out) is semidet.
shortOpt('F', optFoo).
shortOpt('B', optBar).

:- pred longOpt(string::in, myoption::out) is semidet.
longOpt("foo", optFoo).
longOpt("bar", optBar).

:- pred defaultOpt(myoption::out, option_data::out) is multi.
defaultOpt(optFoo, string("buzz")).
defaultOpt(optBar, int(42)).

defaultOpt has to have (out, out) is multi mode, and one uses option_ops_multi instead of option_ops.

Additionally, 0-ary functions won't help you abbreviate clumsy expressions to a single word; forget beta-equivalence.