Next: , Previous: Trace goals, Up: Top   [Contents]

20 Pragmas

The pragma declarations described below are a standard part of the Mercury language, as are the pragmas for controlling the foreign language interface (see Foreign language interface) and impurity (see Impurity). As an extension, implementations may also choose to support additional pragmas with implementation-dependent semantics (see Implementation-dependent extensions).

20.1 Inlining

Declarations of these forms

:- pragma inline(pred(Name/Arity)).
:- pragma inline(func(Name/Arity)).

are a hint to the compiler that all calls to the predicate or function with name Name and arity Arity should be inlined.

The current Mercury implementation is smart enough to inline simple predicates even without this hint.

Declarations of these forms

:- pragma no_inline(pred(Name/Arity)).
:- pragma no_inline(func(Name/Arity)).

tell the compiler not to inline the named predicate or function. This may be used simply for performance concerns (inlining can cause unwanted code bloat in some cases) or to prevent possibly dangerous inlining when using low-level C code.

20.2 Type specialization

The overhead of polymorphism can in some cases be significant, especially where polymorphic predicates make heavy use of class method calls or the builtin unification and comparison routines. To avoid this, the programmer can suggest to the compiler that a specialized version of a procedure should be created for a specific set of argument types.

20.2.1 Syntax and semantics of type specialization pragmas

A declaration of the form

:- pragma type_spec(pred(Name/Arity), Subst).
:- pragma type_spec(func(Name/Arity), Subst).

suggests to the compiler that it should create a specialized version of the predicate or function with name Name and arity Arity with the type substitution given by Subst applied to the argument types. The substitution is written as a conjunction of bindings of the form ‘TypeVar = Type, for example ‘K = int or ‘(K = int, V = list(int)). (The conjunction must have parentheses around it if it contains two or more bindings.)

For example, the declarations

:- pred map.lookup(map(K, V), K, V).
:- pragma type_spec(pred(map.lookup/3), K = int).

give a hint to the compiler that a version of ‘map.lookup/3’ should be created for integer keys.

Implementations are free to ignore ‘pragma type_spec’ declarations. Implementations are also free to perform type specialization even in the absence of any ‘pragma type_spec’ declarations.

The pragma also has a form that suggests specialization of only one mode of the predicate or function, instead of all of them:

:- pragma type_spec(Name(m1, ... mn), Subst).
:- pragma type_spec(Name(m1, ... mn) = mr, Subst).

where m1 etc are the modes of the arguments. If the ‘= mr’ part is present, it gives the mode of the function result; if it is absent, this indicates that Name is a predicate.

20.2.2 When to use type specialization

The set of types for which a predicate or function should be specialized is best determined by profiling your application. Overuse of type specialization will result in code bloat.

Type specialization of predicates or functions which unify or compare polymorphic variables is most effective when the specialized types are builtin types such as int, float and string, or enumeration types, since their unification and comparison procedures are simple and can be inlined.

Predicates or functions which make use of type class method calls may also be candidates for specialization. Again, this is most effective when the called type class methods are simple enough to be inlined.

20.2.3 Implementation specific details

The Melbourne Mercury compiler performs user-requested type specializations when invoked with ‘--user-guided-type-specialization’, which is enabled at optimization level ‘-O2’ or higher. However, for the Java back-end, user-requested type specializations are ignored.

20.3 Obsolescence

Declarations of the forms

:- pragma obsolete(pred(Name/Arity)).
:- pragma obsolete(func(Name/Arity)).
:- pragma obsolete(pred(Name/Arity),
    [ReplName1/ReplArity1, ..., ReplNameN/ReplArityN]).
:- pragma obsolete(func(Name/Arity),
    [ReplName1/ReplArity1, ..., ReplNameN/ReplArityN]).

declare that the predicate or function with name Name and arity Arity is “obsolete”: they instruct the compiler to issue a warning whenever the named predicate or function is used. The forms with a second argument tell the compiler to suggest the use of one of the listed possible replacements.

Declarations of the forms

:- pragma obsolete_proc(PredName(ArgMode1, ..., ArgModeN)).
:- pragma obsolete_proc(PredName(ArgMode1, ..., ArgModeN),
    [ReplName1/ReplArity1, ..., ReplNameN/ReplArityN]).
:- pragma obsolete_proc(FuncName(ArgMode1, ..., ArgModeN) = RetMode).
:- pragma obsolete_proc(FuncName(ArgMode1, ..., ArgModeN) = RetMode,
    [ReplName1/ReplArity1, ..., ReplNameN/ReplArityN]).

similarly declare that the predicate named PredName with arity N, or the function named FuncName with arity N, is obsolete when called in the specified mode. These forms also allow the specification of an optional list of possible replacements.

These declarations are intended for use by library developers, to allow gradual (rather than abrupt) evolution of library interfaces. If a library developer changes the interface of a library predicate or procedure, they should leave its old version in the library, but mark it as obsolete using one of these declarations, and, if possible, use the suggested replacements to steer users to their replacements (either partial or total) in the new interface. The users of the library will then get a warning if they use obsolete features, and can consult the library documentation to determine how to fix their code. Eventually, when the library developer believes that users have had sufficient warning, they can remove the old version entirely.

20.4 No determinism warnings

Declarations of the forms

:- pragma no_determinism_warning(pred(Name/Arity)).
:- pragma no_determinism_warning(func(Name/Arity)).

tell the compiler not to generate any warnings about the determinism declarations of procedures of the predicate or function with name Name and arity Arity not being as tight as they could be.

pragma no_determinism_warning’ declarations are intended for use in situations in which the code of a predicate has one determinism, but the declared determinism of the procedure must be looser due to some outside requirement. One such situation is when a set of procedures are all possible values of the same higher-order variable, which requires them to have the same argument types, modes, and determinisms. If (say) most of the procedures are det but some are erroneous (that is, they always throws an exception), the procedures that are declared det but whose bodies have determinism erroneous will get a warning saying their determinism declaration could be tighter, unless the programmer specifies this pragma for them.

20.5 No dead predicate warnings

Declarations of the forms

:- pragma consider_used(pred(Name/Arity)).
:- pragma consider_used(func(Name/Arity)).

tells the compiler to consider the predicate or function with name Name and arity Arity to be used, and not generate any dead procedure/predicate/function warnings either for the named predicate or function, or for the other predicates and functions that it calls, either directly or indirectly.

pragma consider_used’ declarations are intended for use in situations in which the code that was intended to call such a predicate or function is not yet written.

20.6 Format calls

The ‘format_call’ pragma asks the compiler to perform the same checks on calls to the named predicate or function as it performs for calls to the following formatting predicates and function in the Mercury standard library.

The Mercury standard library has a function and a predicate to format a given sequence of values according to a format string, like this:

    string.format("Count = %d, Total = %5.2f, Average = %5.2f\n",
        [i(Count), f(Total), f(Total/float(Count))], FormatStr)

    FormatStr = string.format("Count = %d, Total = %5.2f, Average = %5.2f\n",
        [i(Count), f(Total), f(Total/float(Count))])

and predicates that immediately output the resulting formatted string, like this:

        "Count = %d, Total = %5.2f, Average = %5.2f\n",
        [i(Count), f(Total), f(Total/float(Count))], !IO)

        "Count = %d, Total = %5.2f, Average = %5.2f\n",
        [i(Count), f(Total), f(Total/float(Count))], !IO)

        "Count = %d, Total = %5.2f, Average = %5.2f\n",
        [i(Count), f(Total), f(Total/float(Count))], !StringWriterState)

These four predicates and one function all take a format string argument, and a values_to_format argument. The format string argument in all the examples above is

        "Count = %d, Total = %5.2f, Average = 5.2%f\n"

while the values_to_format argument is

        [i(Count), f(Total), f(Total/float(Count))]

In this example, %d means that the corresponding value should be output as a signed integer, using as many characters as needed, and %5.2f means that the corresponding value should be formatted as a floating point number, using five characters, of which two are after the decimal point.

Mercury does not allow values of different types to be stored in the same list, so the elements of that list are actually values of the poly_type type, whose definition in the string module of the standard library is

:- type poly_type
    --->    f(float)
    ;       i(int)
    ;       i8(int8)
    ;       i16(int16)
    ;       i32(int32)
    ;       i64(int64)
    ;       u(uint)
    ;       u8(uint8)
    ;       u16(uint16)
    ;       u32(uint32)
    ;       u64(uint64)
    ;       s(string)
    ;       c(char).

As you can see, each function symbol in this type wraps up a value of a builtin type, and thus converts it to the same poly_type type.

With one exception, every occurrence of a percent sign in the format string is intended to start a conversion specifier, which specifies how the corresponding entry in the value list should be converted to a string. (The exception is that the double percent sign %% simply says that the output should be a single percent sign; the doubling up escapes the special conversion-introduction role of the character.) Each conversion specifier consists of a percent sign, zero or more non-alphabetic characters such as ‘5’, and an alphabetic conversion character such as d or f.

The format string and the values list must match. There should be exactly one element in the value list for each conversion specifier, and the Nth value in the value list must satisfy the requirements of the Nth conversion specifier in the format string.

For example, the call

    string.format("Count = %d, Total = %5.2f, Average = %5.2f\n",
        [i(Count), f(Total/float(Count))], FormatStr)

would not work, because the format string contains three conversion specifiers, but the value list contains only two values.

The call

    string.format("Count = %f, Total = %d, Average = %5.2f\n",
        [i(Count), f(Total), f(Total/float(Count))], FormatStr)

would not work either, because the first specifier’s conversion character, f, requires the corresponding value to have the form f(Float), but the corresponding value is i(Count). The conversion character in the second specifier, d, accepts any signed integer as the value, so the second value could be any of i(Int), i8(Int8), i16(Int16), i32(Int32), or i64(Int64), but not f(Float).

For the full set of the rules, please see the documentation of string.format in the Mercury Library Reference Manual; the same rules apply to io.format and stream.string_writer.format) as well.

the Mercury compiler checks calls to the formatting predicates and function in the Mercury standard library, specifically

predicate io.format/4
predicate io.format/5
predicate stream.string_writer.format/5
function string.format/2
predicate string.format/3

for inconsistencies between the format string and the value list, generating a report for each such inconsistency. This improves both program reliability (because programmers get the problem brought to their attention even if the bad call is never executed) and programmer productivity (because programmers don’t have to write test cases just to force the execution of all calls to these predicates). However, the compiler can perform this check only if the predicate or function containing the call to the formatting predicate or function also constructs the format string and the value list. Most calls to formatting predicates satisfy this condition, but not all.

The main exceptions are predicates intended for logging, whose general logic follows this general pattern:

maybe_log_message(LogInfo, FormatString, Values, !IO) :-
    log_info_should_log(LogInfo, ShouldLog),
        ShouldLog = no
        ShouldLog = yes,
        io.format(FormatString, Values, !IO),

In this case, the compiler cannot check the call to io.format in this predicate, because the format string and the values list both come from the caller. If some caller supplies format string and values list arguments that are inconsistent with one another, the call to io.format will throw an exception.

The purpose of the ‘format_call’ pragma is to remedy this. The pragma

:- pragma format_call(pred(maybe_log_message/5),
    format_string_values(2, 3)).

tells the compiler that calls to this predicate should be checked the same way as calls to the four formatting predicates and one formatting function listed the above, with the format string in the second argument, and the values in list in the third. This way, while maybe_log_message cannot ensure that FormatString and Values will match, its callers can do so.

In general, this pragma may take these four forms.

:- pragma format_call(pred(Name/Arity),
    format_string_values(FormatStringArgNum, (ValuesArgNum)).
:- pragma format_call(func(Name/Arity),
    format_string_values(FormatStringArgNum, ValuesArgNum)).
:- pragma format_call(pred(Name/Arity),
    [format_string_values(FS1, V1), ...).
:- pragma format_call(func(Name/Arity),
    [format_string_values(FS1, V1), ...).

The first form is the one in the example above, while the second is the function version. The third and fourth forms differ from the first and second respectively by having the second argument being not a single format_string_values(FormatStringArgNum, ValuesArgNum) term, but a list of two or more such terms. (One such term is also allowed, but in that case, there is no need for the list.) This latter capability is intended for use in situations where predicate or function concerned contains several (possibly conditionally executed) calls to formatting predicates or functions whose format strings and values come from the caller, like this:

:- maybe_quad_log_message(LogInfo,
        FmtA, ValuesA1, ValuesA2,
        FmtB, ValuesB1, ValuesB2, !IO) :-
    io.format(FmtA, ValuesA1, !IO),
    io.format(FmtA, ValuesA2, !IO),
    io.format(FmtB, ValuesB1, !IO),
    io.format(FmtB, ValuesB2, !IO),

In such cases, programmers can include a format_string_values entry describing the argument numbers that act as the sources for each such call, like this:

:- pragma format_call(pred(maybe_quad_log_message/5),
    [format_string_values(2, 3), format_string_values(2, 4),
    format_string_values(5, 6), format_string_values(5, 7)]).

This will ask the compiler to check the compatibility of all four listed argument pairs at all call sites.

20.7 Source file name

The ‘source_file’ pragma and ‘#line’ directives provide support for preprocessors and other tools that generate Mercury code. The tool can insert these directives into the generated Mercury code to allow the Mercury compiler to report diagnostics (error and warning messages) at the original source code location, rather than at the location in the automatically generated Mercury code.

A ‘source_file’ pragma is a declaration of the form

:- pragma source_file(Name).

where Name is a string that specifies the name of the source file.

For example, if a preprocessor generated a file foo.m based on an input file, and it copied lines 20, 30, and 31 from, the following directives would ensure that any error or warnings for those lines copied from foo.m were reported at their original source locations in

:- module foo.
:- pragma source_file("").
% this line comes from line 20 of foo.m
% this line comes from line 30 of foo.m
% this line comes from line 31 of foo.m
:- pragma source_file("foo.m").
% this automatically generated line is line 10 of foo.m

Note that if a generated file contains some text which is copied from a source file, and some which is automatically generated, it is a good idea to use ‘pragma source_file’ and ‘#line’ directives to reset the source file name and line number to point back to the generated file for the automatically generated text, as in the above example.

20.8 Old pragma syntax

Many of the pragmas above specify the identity of a predicate or a function as the entity to which the pragma applies. Their documentation shows these pragmas to have this syntax:

:- pragma pragma_name(pred(Name/Arity)).
:- pragma pragma_name(func(Name/Arity)).

New code should use this syntax. However, old versions of the Mercury compiler supported only a simpler version of this syntax, like this:

:- pragma pragma_name(Name/Arity).

Since this syntax does not specify whether the pragma is supposed to apply to a predicate or to a function, it is ambiguous in the event that the program contains both a predicate and a function with the given name and arity.

For backwards compatibility, the Mercury compiler still supports this syntax, but it will now generate a warning when the program contains both a predicate and a function with the given name and arity. It can also be asked to generate a warning for all pragmas that should specify whether they apply to a predicate or to a function but do not do so.

A later version of Mercury will deprecate this syntax, and a still later version will stop supporting it.

Previous: Source file name, Up: Pragmas   [Contents]