While a ‘pragma foreign_type’ declaration imports a foreign type into Mercury, a ‘pragma foreign_enum’ declaration imports the values of the constants of an enumeration type into Mercury.
While languages such as C have special syntax for defining enumeration types, in Mercury, an enumeration type is simply an ordinary discriminated union type whose function symbols all have arity zero.
Given an enumeration type such as
:- type unix_file_permissions ---> user_read ; user_write ; user_executable ; group_read ; group_write ; group_executable ; other_read ; other_write ; other_executable.
the values used to represent each constant are usually decided by the Mercury compiler. However, the values assigned this way may not match the values expected by foreign language code that uses values of the enumeration, and even if they happen to match, programmers probably would not want to rely on this coincidence.
This is why Mercury supports a mechanism that allows programmers to specify the representation of each constant in an enumeration type when generating code for a given target language. This mechanism is the ‘pragma foreign_enum’ declaration, which looks like this:
:- pragma foreign_enum("C", unix_file_permissions/0, [ user_read - "S_IRUSR", user_write - "S_IWUSR", user_executable - "S_IXUSR", group_read - "S_IRGRP", group_write - "S_IWGRP", group_executable - "S_IXGRP", other_read - "S_IROTH", other_write - "S_IWOTH", other_executable - "S_IXOTH" ]).
(Unix systems have a standard header file that defines each of ‘S_IRUSR’, …, ‘S_IXOTH’ as macros that each expand to an integer constant; these constants happen not to be the ones that the Mercury compiler would assign to those constants.)
The general form of ‘pragma foreign_enum’ declarations is
:- pragma foreign_enum("Lang", MercuryType, CtorValues).
where CtorValues is a list of pairs of the form:
[ ctor_0 - "ForeignValue_0", ctor_1 - "ForeignValue_1", … ctor_N - "ForeignValue_N" ]
The first element of each pair is a constant (function symbol of arity 0) of the type MercuryType, and the second is either a numeric or a symbolic name for the integer value in the language Lang that the programmer wants to be used to represent that constructor.
The mapping defined by this list of pairs must form a bijection, i.e. the list must map distinct constructors to distinct values, and vice versa. The Mercury compiler is not required to check this, because it cannot; even if two symbolic names (such as C macros) are distinct, they may expand to the same integer in the target language.
Mercury implementations may impose further foreign-language-specific restrictions on the form that values used to represent enumeration constructors may take. See the language specific information below for details.
It is an error for any given MercuryType to be the subject of more than one ‘pragma foreign_enum’ declaration for any given foreign language, since that would amount to an attempt to specify two or more (probably) conflicting representations for each of the type’s function symbols.
A ‘pragma foreign_enum’ declaration must occur in the implementation section of the module that defines the type MercuryType.
Note that the default comparison for types that are the subject of a ‘pragma foreign_enum’ declaration will be defined by the foreign values, rather than the order of the constructors in the type declaration (as would otherwise be the case).