Next: Implementation-dependent extensions, Previous: Trace goals, Up: Top [Contents]
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).
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.
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.
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.
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.
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.
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.
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.
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.
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:
io.format( "Count = %d, Total = %5.2f, Average = %5.2f\n", [i(Count), f(Total), f(Total/float(Count))], !IO) io.format(OutputStream, "Count = %d, Total = %5.2f, Average = %5.2f\n", [i(Count), f(Total), f(Total/float(Count))], !IO) stream.string_writer.format(StringWriterStream, "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
io.format/4
io.format/5
stream.string_writer.format/5
string.format/2
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), io.flush_output(!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.
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 foo.m.in, and it copied lines 20, 30, and 31 from foo.m.in, 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 foo.m.in.
:- module foo. :- pragma source_file("foo.m.in"). #20 % this line comes from line 20 of foo.m #30 % this line comes from line 30 of foo.m % this line comes from line 31 of foo.m :- pragma source_file("foo.m"). #10 % 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.
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]