Recent from talks
Nothing was collected or created yet.
Mercury (programming language)
View on Wikipedia| Mercury | |
|---|---|
| Paradigm | Logic, functional, object-oriented[citation needed] |
| Family | Prolog, Haskell |
| Designed by | Zoltan Somogyi |
| Developer | University of Melbourne |
| First appeared | April 8, 1995 |
| Stable release | 22.01.8[1] |
| Typing discipline | strong, static, polymorphic |
| Scope | lexical |
| Implementation language | Mercury |
| Platform | IA-32, x86-64, ARM, SPARC64, Java, CLI |
| OS | Cross-platform: Unix, Linux, macOS, Solaris, FreeBSD, OpenBSD, Windows, Android |
| License | GPL compiler, LGPL standard library |
| Filename extensions | .m |
| Website | www |
| Major implementations | |
| Melbourne Mercury Compiler | |
| Influenced by | |
| Prolog, Hope, Haskell | |
Mercury is a functional logic programming language made for real-world uses. The first version was developed at the University of Melbourne, Computer Science department, by Fergus Henderson, Thomas Conway, and Zoltan Somogyi, under Somogyi's supervision, and released on April 8, 1995.
Mercury is a purely declarative logic programming language. It is related to both Prolog and Haskell.[2] It features a strong, static, polymorphic type system, and a strong mode and determinism system.
The official implementation, the Melbourne Mercury Compiler, is available for most Unix and Unix-like platforms, including Linux, macOS, and for Windows.
Overview
[edit]Mercury is based on the logic programming language Prolog. It has the same syntax and the same basic concepts such as the selective linear definite clause resolution (SLD) algorithm. It can be viewed as a pure subset of Prolog with strong types and modes. As such, it is often compared to its predecessor in features and run-time efficiency.
The language is designed using software engineering principles. Unlike the original implementations of Prolog, it has a separate compilation phase, rather than being directly interpreted. This allows a much wider range of errors to be detected before running a program. It features a strict static type and mode system[2] and a module system.
By using information obtained at compile time (such as type and mode), programs written in Mercury typically perform significantly faster than equivalent programs written in Prolog.[3][4] Its authors claim that Mercury is the fastest logic language in the world, by a wide margin.[2]
Mercury is a purely declarative language, unlike Prolog, since it lacks extra-logical Prolog statements such as ! (cut) and imperative input/output (I/O). This enables advanced static program analysis and program optimization, including compile-time garbage collection,[5] but it can make certain programming constructs (such as a switch over a number of options, with a default[dubious – discuss]) harder to express. While Mercury does allow impure functionality, it serves mainly as a way to call foreign language code. All impure code must be explicitly marked. Operations which would typically be impure (such as input/output) are expressed using pure constructs in Mercury using linear types, by threading a dummy world value through all relevant code.
Notable programs written in Mercury include the Mercury compiler and the Prince XML formatter. The Software company ODASE has also been using Mercury to develop its Ontology-Centric software development platform, ODASE.[6]
Back-ends
[edit]Mercury has several back-ends, which enable compiling Mercury code into several languages, including:
Production level
[edit]- Low-level C for GNU Compiler Collection (GCC), the original Mercury back-end
- High-level C
- Java
- C#
Past
[edit]- Assembly language via the GCC back-end
- Aditi, a deductive database system also developed at the University of Melbourne. Mercury-0.12.2 is the last version to support Aditi.[citation needed]
- Common Intermediate Language (CIL) for the .NET Framework
- Erlang
Mercury also features a foreign language interface, allowing code in other languages (depending on the chosen back-end) to be linked with Mercury code. The following foreign languages are possible:
| Back-end | Foreign language(s) |
|---|---|
| C (both levels) | C |
| Java | Java |
| Erlang | Erlang |
| IL | Common Intermediate Language (CIL) or C# |
Other languages can then be interfaced to by calling them from these languages. However, this means that foreign language code may need to be written several times for the different backends, otherwise portability between backends will be lost.
The most commonly used back-end is the original low-level C back-end.
Examples
[edit] :- module hello.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
main(!IO) :-
io.write_string("Hello, World!\n", !IO).
Calculating the 10th Fibonacci number (in the most obvious way):[7]
:- module fib.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module int.
:- func fib(int) = int.
fib(N) = (if N =< 2 then 1 else fib(N - 1) + fib(N - 2)).
main(!IO) :-
io.write_string("fib(10) = ", !IO),
io.write_int(fib(10), !IO),
io.nl(!IO).
% Could instead use io.format("fib(10) = %d\n", [i(fib(10))], !IO).
!IO is a "state variable", which is syntactic sugar for a pair of variables which are assigned concrete names at compilation; for example, the above is desugared to something like:
main(IO0, IO) :-
io.write_string("fib(10) = ", IO0, IO1),
io.write_int(fib(10), IO1, IO2),
io.nl(IO2, IO).
Release schedule
[edit]The stable release naming scheme was 0.1 up to 0.13 for the first thirteen stable releases. In February 2010 the Mercury project decided to name each stable release by using the year and month of the release. For example 10.04 is for a release made in April 2010.
There is often also a periodic snapshot of the development system release of the day (ROTD)
IDE and editor support
[edit]- Developers provide support for Vim
- Flycheck library for Emacs
- A plugin is available for the Eclipse IDE
- A plugin is available for the NetBeans IDE
See also
[edit]- Curry, another functional logic language
- Alice, a dialect language of Standard ML
- Logtalk, language, an object-oriented extension of Prolog which compiles down to Prolog
- Oz/Mozart, a multiparadigm language
- Visual Prolog, language, a strongly typed object-oriented extension of Prolog, with a new syntax
References
[edit]- ^ "Release 22.01.8". 8 September 2023. Retrieved 18 September 2023.
- ^ a b c The Mercury Project - Motivation
- ^ The Mercury Project - Benchmarks
- ^ Somogyi, Zoltan; Henderson, Fergus; Conway, Thomas (October–December 1996). "The execution algorithm of Mercury: an efficient purely declarative logic programming language". Journal of Logic Programming. 29 (1–3). Mercurylang.org: 17–64. CiteSeerX 10.1.1.46.9861. doi:10.1016/S0743-1066(96)00068-4. Retrieved 2008-08-30.
- ^ Mazur, Nancy (May 2004). Compile-time garbage collection for the declarative language Mercury (PDF) (Thesis). Katholieke Universiteit Leuven.
- ^ ODASE
- ^ Adapted from Ralph Becket's Mercury tutorial
External links
[edit]Mercury (programming language)
View on GrokipediaIntroduction
Overview
Mercury is a purely declarative functional logic programming language designed for the development of reliable, high-performance applications in real-world scenarios.[1] It integrates the declarative expressiveness of logic programming, akin to Prolog, with functional programming elements, while prioritizing advanced static analysis to detect errors and enable optimizations at compile time.[6] This approach allows programmers to focus on what the program should accomplish rather than how, reducing common sources of bugs through rigorous checking mechanisms.[3] The language's core goals emphasize modularity for large-scale software construction, support for separate compilation to facilitate team development, and generation of efficient code that rivals the performance of imperative languages such as C.[6] Mercury achieves this through features like strong typing, mode analysis, and determinism declarations, which collectively ensure predictable behavior and high runtime efficiency without sacrificing clarity.[1] Mercury runs on a variety of platforms, including Unix-like systems such as Linux and macOS, Windows, and Android via its Java backend.[7][8] The compiler and associated tools are distributed under the GNU General Public License (GPL), while the standard library falls under the GNU Lesser General Public License (LGPL) to permit flexible integration.[9] Development of Mercury originated at the University of Melbourne's Department of Computer Science and Software Engineering.[3]History
Mercury was initiated in October 1993 by a design team at the University of Melbourne's Department of Computer Science and Software Engineering, aiming to create a logic programming language that addressed the shortcomings of existing systems like Prolog for large-scale, industrial-strength applications.[10] The project was led by Zoltan Somogyi, with key contributions from Fergus Henderson and Thomas Conway, who focused on incorporating strong static analysis to enhance error detection and performance.[10][11] Development of the first compiler began in December 1993, initially bootstrapping from NU-Prolog and later SICStus Prolog, with semantic analysis completed by May 1994 and code generation starting in August 1994.[10] A significant milestone was achieved on February 24, 1995, when the compiler successfully compiled itself, marking the transition to self-hosting and eliminating reliance on external Prolog systems.[10] The motivation stemmed from Prolog's limitations, including weak compile-time error checking that led to runtime failures and suboptimal performance compared to imperative languages, making it unsuitable for reliable, efficient software development in industrial contexts.[10][12] The first beta release (version 0.1) occurred on April 8, 1995, followed by the initial public beta (version 0.3) on July 18, 1995, introducing core features like compilation to C for portability.[10][13] Subsequent releases in the late 1990s, such as version 0.7 in August 1997, added optimizations and interfaces, solidifying Mercury's maturity.[10] Early adoption included the Mercury compiler itself, demonstrating its practicality, as well as the Prince XML formatter developed by YesLogic and the ODASE ontology-based application platform.[14] In the 1990s, the project shifted to an open-source model under the GNU General Public License (GPL), with some components later using the GNU Lesser General Public License (LGPL) to facilitate broader integration.[9][15]Design and Features
Paradigms and Goals
Mercury is a multi-paradigm programming language that primarily combines logic and functional paradigms, enabling declarative specifications of computations through predicates and higher-order functions, while incorporating optional imperative elements for stateful operations like input/output and support for object-oriented programming styles through type classes and existential types.[1][4] The foundational goals of Mercury address key weaknesses in Prolog, such as poor static error detection—where type errors often manifest only at runtime—and relatively slow execution speeds for large applications.[10] By integrating strong static analysis, including types and modes, Mercury catches a substantial portion of errors during compilation, promoting reliability in industrial-scale software.[10] In early benchmarks from the 1990s, it achieved up to 36 times the speed of bytecode-interpreted Prolog systems and nearly twice that of optimized native-code Prologs like Aquarius, approaching the efficiency of imperative languages such as C or Ada through its generation of portable, optimized C code and compile-time garbage collection.[10][16] Mercury prioritizes declarative programming to foster clarity and expressiveness in complex, large-scale systems, where developers describe desired outcomes rather than step-by-step procedures.[1] This is bolstered by a commitment to purity, eschewing hidden side effects in favor of explicit state passing, which facilitates optimizations like targeted garbage collection and reduced backtracking overhead.[4] Yet, to ensure practicality, Mercury allows controlled impurities for essential tasks, tempered by syntactic conveniences that ease the handling of state variables without compromising core optimizations.[4] The design emphasizes modularity to support programming in the large, with separate compilation per module and localized optimization decisions that enhance maintainability and scalability in team-based development.[10] Modes contribute to these objectives by refining data flow analysis at compile time, as explored further in the Mode and Determinism Analysis section.[10]Type System
Mercury features a strong, static, polymorphic type system based on many-sorted logic, which ensures type safety at compile time while supporting expressive programming constructs.[17] This system includes higher-order types, allowing functions and predicates to be treated as first-class values, such aspred(int, [string](/page/String)) for a predicate taking an integer and string, or func(T) = U for a polymorphic function type.[17] User-defined types enable programmers to create custom data structures, including equivalence types (e.g., :- type [money](/page/Money) == int), subtypes for inheritance-like hierarchies (e.g., :- type citrus_fruit =< fruit), and abstract types that hide implementation details by declaring only the type name in the module interface (e.g., :- type t1).[17]
Polymorphism is achieved through type variables, which are universally quantified by default, permitting generic code that works across multiple types. For instance, the built-in list(T) type uses a type variable T to represent lists of any element type, as in :- pred member(T, list(T)), allowing instantiation with specific types like list(int) or list(float).[17] The compiler performs type inference to determine the most general type for expressions and predicates, automatically catching type mismatches—such as passing a string to a function expecting an integer—before runtime, thereby preventing a wide class of errors.[17]
Mercury also supports type classes for ad-hoc polymorphism, allowing the definition of interfaces for types, similar to those in Haskell, which enable object-oriented programming styles. For example, an eq type class can define equality operations for custom types: :- typeclass eq(T) where pred (==)(T, T, comparison_result). This feature extends polymorphism beyond parametric types, supporting overloading and modular type definitions.[17]
Mercury supports existential types for abstracting over specific type instances within a scope, declared using some [T] quantification, as in :- type maybe_foo ---> some [T] foo(T) => fooable(T), which allows values of unknown but constrained types.[17] Discriminated unions, akin to algebraic data types, are defined with multiple constructors using the --> functor, enabling sum types like :- type [tree](/page/Tree)(T) ---> empty ; node(T, [tree](/page/Tree)(T), [tree](/page/Tree)(T)) for binary trees with polymorphic nodes, or labeled variants such as employee(name :: string, age :: int).[17] For resource management, particularly I/O, Mercury employs linear types through uniqueness constraints on the io.state type (e.g., main(!IO)), ensuring mutable resources like file handles are consumed exactly once to avoid leaks or concurrent access issues.[17]
These features collectively reduce runtime errors by enforcing type correctness statically and facilitate compiler optimizations, such as dead code elimination, by providing precise type information that, in conjunction with other analyses, identifies unused code paths.[17]
Mode and Determinism Analysis
Mercury's mode system extends its static analysis beyond types by specifying the direction and state of data flow for procedure arguments, enabling the compiler to perform precise checks and optimizations at compile time. Programmers declare modes for each argument of a predicate or function, indicating whether it serves as input or output and its expected instantiation state. Common modes includein for inputs that must be ground (fully instantiated) upon entry, out for outputs that are free (uninstantiated) upon entry and become ground upon success, di (destructively input) for unique arguments consumed during execution without backtracking, and uo (destructively output) for unique arguments produced as a result. These declarations allow the compiler to track data dependencies and reorder goals in conjunctions to satisfy mode constraints, ensuring that no argument is used before it is sufficiently instantiated.[17]
The system also incorporates advanced modes to manage mutability and aliasing. Instability modes, such as mostly_unique, track whether an argument's state changes can be safely reversed on backtracking, preventing inconsistencies in trailed updates. Uniqueness modes enforce that only a single reference to a value exists, allowing safe destructive updates without aliasing risks; for instance, a di mode transitions a unique input to dead (no live references), facilitating efficient in-place modifications like I/O state updates. By analyzing these modes, the compiler detects errors such as attempts to read uninitialized variables or create unintended aliases, rejecting ill-moded programs before runtime.[17]
Complementing modes is Mercury's determinism analysis, which categorizes procedure outcomes based on the number of solutions and potential for failure. The compiler infers determinism categories for each mode, enabling further static verification and code tailoring.
| Category | Description | Solutions | Can Fail? |
|---|---|---|---|
det | Succeeds exactly once without failure. | Exactly 1 | No |
semidet | Succeeds at most once, possibly failing. | 0 or 1 | Yes |
multi | Succeeds more than once without failure. | >1 | No |
nondet | Succeeds zero or more times, possibly failing. | ≥0 | Yes |
cc_multi | Committed choice; succeeds more than once without failure. | >1 | No |
cc_nondet | Committed choice; succeeds zero or more times, possibly failing. | ≥0 | Yes |
erroneous | Never succeeds and may throw an exception or loop indefinitely. | 0 | N/A (aborts) |
failure | Always fails without exceptions or loops. | 0 | Yes |
is det and influence control flow; for example, det goals can be executed in loops without backtracking overhead, while nondet goals require search mechanisms.[18][17]
During compilation, mode and determinism analysis are tightly integrated: the compiler propagates instantiation states forward and backward through the call graph, inferring missing declarations where possible and flagging inconsistencies. This process optimizes execution by avoiding unnecessary backtracking in deterministic contexts—for instance, generating iterative loops for det predicates instead of recursive search—and eliminates runtime checks for mode-correct programs. Errors like over- or under-instantiation are caught early, enhancing reliability. Seminal work on determinism inference, as detailed in the Mercury compiler's analysis algorithms, ensures scalable propagation across modules while handling higher-order code.[17][19]
Overall, these analyses yield substantial benefits, including improved performance through specialized code generation (e.g., direct assignment for out modes versus allocation for nondestructive ones) and guaranteed predictable behavior in concurrent or real-time applications. By preventing aliasing via uniqueness and ensuring mode-correct data flow, Mercury achieves high efficiency comparable to imperative languages while retaining declarative purity.[17][20]
Purity and Modules
Mercury employs a purity system to control side effects, distinguishing three levels: pure, semipure, and impure. Pure code is referentially transparent, exhibiting no observable side effects and adhering strictly to logical semantics, which allows multiple calls with the same inputs to yield identical outputs without altering external state. Semipure code permits limited, read-only interactions with the external world, such as querying the current time or reading input without modification, ensuring determinism but allowing observation of hidden state. Impure code supports full side effects, including mutation, I/O operations that change state, or calls to non-declarative foreign procedures, but requires explicit handling to prevent unintended propagation.[21] The compiler enforces purity through explicit annotations on predicates and functions, using keywords likeimpure and semipure to declare levels, while pure is the default. Any use of impure or semipure constructs within a procedure propagates the lower purity level unless overridden by purity casts, which the compiler verifies to isolate effects. This tracking confines impure code to designated regions, enabling aggressive optimizations in pure sections, such as deforestation to eliminate intermediate data structures and loop detection for tail-call optimization, thereby improving efficiency without compromising reliability.[21][22]
Mercury's module system facilitates large-scale development by separating interfaces from implementations, promoting separate compilation and information hiding. Each module consists of an interface file (.int), which exports types, predicates, functions, and modes visible to other modules, and an implementation file (.m), containing the private definitions and logic. This structure supports encapsulation, where internal details remain hidden, enhancing reusability and maintainability while allowing the compiler to generate optimized object code independently for each module. Submodules, either nested or separate, extend this organization for hierarchical designs.[3][23]
Integration of foreign code, such as C procedures, occurs via pragmas that mark them as impure, ensuring side effects are contained and do not leak into pure modules without declaration. This approach preserves Mercury's declarative core for most computations while accommodating practical necessities like system I/O. Overall, the purity and module systems together maintain a declarative programming style, enabling reliable large programs with controlled effects, modular abstraction, and compiler-driven performance gains.[21][23]
Implementation
Compiler Architecture
The Melbourne Mercury Compiler (MMC) employs a multi-phase architecture that processes Mercury source code through distinct front-end, middle-end, and back-end stages to ensure correctness, efficiency, and portability. The front-end handles parsing and semantic analysis, transforming source files into a high-level intermediate representation (HLDS), while the middle-end applies optimizations to this representation, and the back-end generates target code from the optimized form. This modular design, implemented in Mercury itself, enables self-hosting, where the compiler compiles its own source code, leveraging its analyses to detect and prevent bugs during development.[22][24] Parsing begins with the scanner and parser modules, which convert Mercury syntax in.m files into an abstract syntax tree represented as parse trees. This is followed by semantic analysis in the front-end, which performs type checking to verify type correctness across the program, mode analysis to determine input/output modes of predicates ensuring proper argument flow, determinism analysis to classify procedures by their success or failure behavior (e.g., deterministic or semideterministic), and purity analysis to identify side-effect-free procedures. These checks, conducted on the HLDS, enforce the language's declarative guarantees and provide foundational data for subsequent optimizations, with errors reported early to aid development.[22][24]
In the middle-end, the compiler applies high-level transformations to the HLDS, including higher-order specialization, which generates specialized versions of predicates when higher-order arguments are known at compile time, and deforestation, which eliminates unnecessary intermediate data structures to reduce memory allocation and improve runtime performance. Additional optimizations encompass compile-time garbage collection, an experimental feature that statically identifies and removes unreachable data to minimize runtime overhead, and intelligent backtracking avoidance, which uses determinism information to prune non-viable execution paths and prevent unnecessary retries. These transformations produce an optimized HLDS, serving as the primary intermediate representation before translation to lower-level forms.[22][24]
The compiler's self-hosting nature and advanced analyses contribute to its robustness, as demonstrated by the detection of issues in the compiler codebase through its own type, mode, and determinism checks during bootstrapping. For portability, the generated intermediate representations facilitate targeting multiple platforms by allowing backend translations to produce code compatible with various runtime environments, without platform-specific modifications in the core pipeline. Optimizations like those leveraging purity annotations enable further refinements, such as inlining pure functions, enhancing overall efficiency.[22][24]
Backends
The Mercury compiler primarily utilizes two C-based backends as its default targets for code generation, optimized for high performance across a wide range of platforms. The low-level C backend, based on the Low-Level Data Structure (LLDS) intermediate representation, translates Mercury code into an abstract machine model resembling assembly, using custom registers, stacks, and heap management, which is then emitted as low-level C code often relying on GNU C extensions for efficiency.[22][25] This backend supports aggressive optimizations such as jump optimization, dead code elimination, and peephole optimization, enabling fine-grained control over the generated code but resulting in more complex output that can lead to larger binaries due to the explicit management of low-level constructs.[22] In contrast, the high-level C backend, generated via the Medium-Level Data Structure (MLDS) intermediate representation, produces more readable C code that mimics human-written imperative structures, delegating memory management and optimizations to the C compiler itself, which simplifies the backend at the cost of potentially less tailored performance in certain scenarios.[22][25] In addition to the C backends, Mercury supports other production backends for integration with specific ecosystems. The Java backend, also using MLDS, compiles Mercury code to standard Java source files, facilitating seamless interoperability with the JVM and access to Java's extensive libraries, though it currently lacks built-in profiling support.[25][22] Similarly, the C# backend targets the .NET framework by generating C# code, allowing for C# interoperability and debugging, with compatibility for both Microsoft .NET and Mono runtimes, but sharing the limitation of no native profiling.[25][8] The Erlang backend compiles a subset of Mercury to Erlang code, enabling execution on the Erlang runtime system to leverage its strengths in concurrency and fault-tolerant distributed systems, with good support for interfacing between Mercury and Erlang modules.[8][7] These non-C backends prioritize ecosystem access and portability over raw speed, often employing more conservative optimization levels compared to the C paths, as they rely on the target language's runtime for many low-level details.[25][22] Historically, the MLDS served as a key intermediate representation for targeting multiple languages including C, Java, and C#, streamlining code generation across backends by focusing on higher-level transformations like eliminating nested functions and unused assignments.[22] Experimental efforts have included direct generation to .NET Intermediate Language (IL), which was supported in earlier versions but is no longer maintained, shifting focus to the C# backend for .NET compatibility.[22] Bytecode interpreters were considered for rapid prototyping but were ultimately discontinued due to performance limitations in production environments.[22] As of 2024, the C backends remain the dominant choice for most Mercury applications, benefiting from ongoing updates for compatibility with modern compilers, such as adjustments to low-level C generation to accommodate restrictions in recent GCC versions on GNU extensions.[25][8] The Java and C# backends continue to see use for cross-platform integration, while the Erlang backend persists for specialized concurrent applications, though all non-C targets have minor gaps in standard library coverage compared to native Mercury implementations.[25][7]Foreign Language Interfaces
Mercury's foreign language interface (FLI) enables the integration of code written in other programming languages with Mercury programs, allowing developers to leverage existing libraries and implement specialized functionality that may not be efficiently expressible in Mercury alone.[17] This interface is particularly useful for incorporating imperative or low-level code while maintaining Mercury's declarative strengths, though it requires explicit declarations of impurity for procedures that interact with foreign code.[17] The primary mechanisms for embedding foreign code are pragmas such asforeign_proc, foreign_code, and foreign_decl. The pragma foreign_proc allows a Mercury predicate or function to be implemented directly in a foreign language, specifying the language, argument modes, and the code body. For example, in C: :- pragma foreign_proc("C", add(X::in, Y::in, Z::out), [will_not_call_mercury], "Z = X + Y;").[17] The pragma foreign_code inserts raw foreign language snippets into the generated output file, useful for auxiliary code like initializations. An example is :- pragma foreign_code("C", "#include <stdio.h>").[17] Similarly, pragma foreign_decl adds declarations, such as external function prototypes, to the interface file: :- pragma foreign_decl("C", "extern int my_function(int);").[17] Additional pragmas like foreign_export enable Mercury procedures to be callable from foreign code by specifying an export name.[17]
Mercury supports interfaces to several languages, with C offering the most mature implementation, followed by Java, C# (via Intermediate Language or IL), and Erlang.[17] For Java, mappings include Mercury integers to Java int and strings to java.lang.String, as in: :- pragma foreign_proc("Java", print_str(S::in), [], "System.out.println(S);").[17] In C#, types map to .NET equivalents, such as System.Int32 for integers.[17] These interfaces require foreign procedures to be marked as impure or semipure, as they may perform side effects outside Mercury's purity system.[17]
Data representation and marshalling between Mercury and foreign types are handled through language-specific conventions, with automatic conversions for primitive types but manual intervention for complex structures. In C, Mercury uses MR_Word for generic terms and pointers via c_pointer; lists may be represented as head-tail structures with macros like MR_list_head.[17] Trailing mechanisms, such as MR_trail_value, ensure updates are reversible during backtracking, though this adds overhead.[17] For arrays or enums, pragmas like foreign_type and foreign_enum define custom mappings to maintain compatibility.[17]
Common use cases include accessing system calls, integrating with GUI toolkits like GTK via C bindings, or utilizing numerical libraries such as BLAS without native Mercury wrappers.[17] This allows Mercury programs to interface with vast ecosystems of foreign libraries, enhancing practicality for real-world applications.[17]
Limitations arise from the loss of Mercury's static analyses in foreign code, including type checking, mode analysis, and determinism guarantees across boundaries.[17] Developers must manually handle memory management, error propagation, and exceptions, as no automatic support exists for throwing across the C interface, for instance.[17] Multi- and nondeterministic foreign procedures are unsupported, requiring fallback to Mercury clauses, and garbage collection does not manage trail entries, potentially leading to leaks.[17]
Tools and Ecosystem
Development Tools
The primary tool for compiling Mercury programs is themmc command, which serves as the front-end compiler driver. It handles the translation of Mercury source code into intermediate representations and subsequent backends, with extensive options for customization. Optimization levels are controlled via the -O n flag, where n ranges from -1 (disabling all optimizations) to 6 (enabling the full suite, including advanced inter-module analyses); level 2 is the default, balancing compilation speed and performance gains.[24] Debugging support is activated with the --debug option, which instruments the code for use with the Mercury debugger and is compatible with grades such as asm_fast.gc.debug. Backend selection is managed through the --target or --grade options, supporting targets like C (default via hlc or asm_fast), Java, and C#, allowing developers to choose based on deployment needs.[24]
For managing larger projects, Mercury provides mmake, a build system analogous to Make but tailored for Mercury's module dependencies. It automates the compilation, linking, and dependency resolution for multi-module programs by generating files like .dep (dependencies) and .dv (source dependencies) from an MMakefile. Developers invoke it as mmake <main_module> for C-targeted builds or use variables such as MCFLAGS and MLFLAGS in the MMakefile to specify compiler and linker flags, including support for profiling via GRADEFLAGS=--profiling. This ensures efficient incremental builds while handling Mercury-specific features like interface files.[24]
Debugging is facilitated by mdb, the Mercury interactive debugger, which allows stepping through predicate executions and inspecting program state. Key commands include step [num] to advance through events (such as calls, exits, redos, or fails), next for skipping into subgoals, continue to resume until a breakpoint, and finish to complete the current procedure. Inspection features encompass print * for live variables, browse for examining modes and values, and stack or d for viewing the call stack, with full functionality requiring a .debug or .decldebug grade. This tool is particularly useful for logic programs, enabling mode-aware debugging.[24]
Performance analysis is supported by built-in profilers, including mprof for measuring time and memory allocation/retention per predicate, and mdprof for deep profiling that generates browser-viewable reports from Deep.data files. These tools track metrics like execution time, space usage, and call counts, aiding optimization of logic and functional components; parallel execution profiling is also available. Compilation with --profiling or --deep-profiling enables these features.[24]
Mercury includes support for unit testing through the require module in its standard library, which provides assertion-like predicates such as expect/3 (which aborts with an error message if a goal fails) and require/2 (similarly enforcing goals at runtime). These can be integrated into test predicates to verify behavior, often combined with the solutions module for collecting and checking outputs from nondeterministic code. While not a full-fledged framework, this enables straightforward unit tests within Mercury modules.[26]
Installation of Mercury typically involves downloading a Release of the Day (ROTD) snapshot, which captures the latest development version in the format rotd-YYYY-MM-DD from the official download site. These source distributions require bootstrapping with an existing Mercury installation and are built using a configure script, with options like --install-prefix <dir> for the installation directory and --libgrade <grade> for platform-specific grades (e.g., posix or windows). Platform configuration adjusts for operating systems and architectures via environment variables or the --env-type flag, ensuring compatibility across Unix-like systems, Windows, and others. Full setup details are in the bootstrap README.[9][27][24]
Editor and IDE Support
Mercury offers editor support primarily through developer-provided and community-maintained plugins, emphasizing syntax highlighting, indentation, and basic integration with the Mercury compiler (mmc) for tasks like compilation and error navigation. While IDE integration exists, it is sparse and dated compared to mainstream languages, with no comprehensive modern IDE available. Developers typically rely on lightweight text editors augmented by these tools, often drawing from Prolog-inspired modes due to Mercury's logic programming roots. In Emacs, the Metal Mercury Mode serves as a dedicated major mode for editing Mercury files, addressing shortcomings in the default mercury-mode (derived from prolog-mode) by providing robust syntax highlighting, proper indentation, and keybindings such as C-c C-c for compiling the current file viammc --make and C-c C-r for compiling and running without interactive input.[28] The Flycheck library extends this with on-the-fly syntax checking and error display during editing.[29] Additionally, Mercury's official documentation describes an Emacs interface for the debugger, which displays source code in a buffer and highlights the currently executing line as tracing progresses.[30]
Vim receives official support from the Mercury developers, distributed via the vim directory in release packages, which includes syntax files for highlighting Mercury constructs, filetype plugins for automatic mode detection, and basic commands for tags and navigation.[29] Community efforts, such as the stewy33/mercury-vim fork, build on this by adding enhanced commands, improved error handling, and comprehensive help documentation accessible via :help mercury-syntax and related tags.[31]
For other text editors, Visual Studio Code has community-driven extensions like vscode-mercury, which delivers essential syntax highlighting for Mercury keywords, modules, and predicates to improve code readability.[32] The mercury-ls language server further enables features such as diagnostics, hover information, and error navigation by integrating with the Mercury compiler's output.[33] Sublime Text supports Mercury through a TextMate-compatible grammar, enabling syntax highlighting via community packages.[34] The Atom editor also has an official Mercury package for similar basic editing features, though Atom's development has ceased as of 2022.[29]
IDE support for Mercury is limited to older plugins for Eclipse and NetBeans, which offer project creation, single-file compilation, syntax highlighting, and integration with compiler errors for navigation.[29] The NetBeans plugin, for instance, targets versions 6.9 through 8.2 across Windows, Linux, and macOS, functioning as part of the broader Mercury Development Environment for streamlined workflows.[35] These tools lack the advanced refactoring, debugging, or autocompletion found in contemporary IDEs for languages like Java. Community repositories on GitHub, active into 2024, host these and other extensions, filling gaps through volunteer contributions.[7] No official plugins exist for IntelliJ IDEA, leaving developers to adapt general logic programming tools or generic language servers.[29]
Standard Library
The Mercury standard library comprises over 115 modules, organized into categories based on functionality such as data structures, input/output operations, exception handling, string and numeric manipulations, and utilities.[26] These modules are housed in the library directory of the Mercury distribution and are documented with details on types, predicates, functions, and stability ratings (low, medium, or high) to indicate potential interface changes in future versions.[26] Core data structure modules includelist for common list manipulations like appending and folding; assoc_list for simple key-value associations functioning as dictionaries; map for efficient key-value storage and lookup; and set variants such as set_tree234 for ordered sets of unique elements.[26] For input/output and system interactions, the io module provides essential predicates for file handling and printing, utilizing state variables denoted by !IO to manage input/output state in a controlled manner.[26] The getopt module supports command-line option parsing, enabling programs to process arguments with short and long options.[26] Additionally, the exception module handles error propagation and recovery through predicates for throwing and catching exceptions.[26]
Higher-level utility modules cover type conversions and conditional features, with string, char, and int providing operations for string manipulation, character processing, and integer arithmetic including conversions between types.[26] The require module facilitates assertions and conditional compilation via predicates like error/1 for runtime checks and unexpected/2 for handling invalid states.[26]
Most modules in the standard library are pure, ensuring side-effect-free and deterministic behavior suitable for declarative programming; impure modules, primarily those involving I/O or external interactions like io and stream, explicitly manage side effects through dedicated modes such as di (destroyed input) and uo (unique output).[26]
The library is licensed under the GNU Library General Public License (LGPL) version 2, with a special exception permitting static or dynamic linking with proprietary code to form executables distributable under chosen terms, provided appropriate notices and the license text are included.[36] Updates in the 22.01 series, culminating in version 22.01.8 released on 17 September 2023, have refined module interfaces and added stability notes without major structural changes to the library organization.[1][26]
Examples and Applications
Basic Syntax Examples
Mercury's syntax draws from logic programming languages like Prolog while incorporating strong static typing and declarative constructs. Basic programs are structured within modules, which separate interface declarations from implementations. The language uses predicates to define relations and procedures, with input/output handled through state-passing via the!IO type.[37]
A simple "Hello World" program demonstrates the essential module structure, I/O import, and main predicate. The following code defines a module named hello, imports the io module for input/output operations, declares the main predicate in the interface, and implements it to print the message using the stateful !IO variable.
:- module hello.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
main(!IO) :-
io.write_string("Hello, World!\n", !IO).
:- module hello.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
main(!IO) :-
io.write_string("Hello, World!\n", !IO).
mmc --make hello, which processes the file hello.m and generates an executable.[37]
For arithmetic operations, Mercury defines predicates with explicit modes indicating input (in) and output (out) arguments. A basic addition predicate might look like this, where the sum is computed deterministically:
:- pred add(int::in, int::in, int::out) is det.
add(X, Y, Z) :-
Z = X + Y.
:- pred add(int::in, int::in, int::out) is det.
add(X, Y, Z) :-
Z = X + Y.
X and Y are inputs, and Z binds to their sum. This illustrates Mercury's use of unification (=) for assignment and computation.[37]
List processing in Mercury leverages its logic programming roots for search-based operations. The member predicate exemplifies nondeterministic search, succeeding multiple times if the element appears in the list:
:- pred member(T::out, list(T)::in) is nondet.
member(X, [X | _]).
member(X, [_ | Xs]) :-
member(X, Xs).
:- pred member(T::out, list(T)::in) is nondet.
member(X, [X | _]).
member(X, [_ | Xs]) :-
member(X, Xs).
X to each element in the input list, demonstrating pattern matching on list cons cells ([Head | Tail]) and backtracking for multiple solutions.[37]
Mercury source files use the .m extension and follow a standard structure starting with module declaration, followed by the interface section for public predicates, and the implementation section for definitions. An example skeleton is:
:- module my_module.
:- interface.
:- import_module [io](/page/.io).
:- pred my_pred([io](/page/.io)::di, [io](/page/.io)::uo) is det.
:- implementation.
:- import_module require. % For error handling if needed
my_pred(!IO) :-
io.write_string("Module example\n", !IO).
:- module my_module.
:- interface.
:- import_module [io](/page/.io).
:- pred my_pred([io](/page/.io)::di, [io](/page/.io)::uo) is det.
:- implementation.
:- import_module require. % For error handling if needed
my_pred(!IO) :-
io.write_string("Module example\n", !IO).
Advanced Usage Examples
Mercury's advanced features enable efficient implementations of complex algorithms through declarative constructs. One common optimization leverages memoization for recursive computations, such as calculating Fibonacci numbers, by declaring predicates as tabled and using deterministic modes to ensure purity and avoid redundant calls. The following example defines a tabled predicate for the nth Fibonacci number, utilizing higher-order aspects implicitly through the language's mode system for input-output behavior.:- pred fib(int::in, int::out) is det.
:- pragma tabled(fib/2).
fib(0, 0).
fib(1, 1).
fib(N, F) :-
N > 1,
fib(N-1, F1),
fib(N-2, F2),
F = F1 + F2.
:- pred fib(int::in, int::out) is det.
:- pragma tabled(fib/2).
fib(0, 0).
fib(1, 1).
fib(N, F) :-
N > 1,
fib(N-1, F1),
fib(N-2, F2),
F = F1 + F2.
:- func sin(float) = float.
:- mode sin(in) = out is det.
:- promise pure(sin/1).
:- pragma foreign_proc("C",
sin(X::in) = (Result::out),
[promise_pure],
"Result = sin(X);").
:- func sin(float) = float.
:- mode sin(in) = out is det.
:- promise pure(sin/1).
:- pragma foreign_proc("C",
sin(X::in) = (Result::out),
[promise_pure],
"Result = sin(X);").
in and output out, ensuring the result is assigned directly without Mercury runtime calls. This approach is ideal for numerical libraries where C's optimized math routines outperform native implementations.[38]
Module interactions highlight Mercury's modular design, importing standard library components like the list module for higher-order functions such as foldl. Consider summing elements in a list of integers, where foldl applies an accumulator predicate left-to-right, demonstrating mode inference for destructive input-output updates.
:- import_module list.
:- pred sum_list([list](/page/List)(int)::in, int::out) is det.
sum_list(L, Sum) :-
list.foldl((pred(X::in, Acc0::di, Acc::uo) is det :- Acc = Acc0 + X), L, 0, Sum).
:- import_module list.
:- pred sum_list([list](/page/List)(int)::in, int::out) is det.
sum_list(L, Sum) :-
list.foldl((pred(X::in, Acc0::di, Acc::uo) is det :- Acc = Acc0 + X), L, 0, Sum).
(pred(X::in, Acc0::di, Acc::uo) is det :- ...) acts as a higher-order argument to foldl, accumulating the sum from an initial value of 0. For a list [1, 2, 3], this yields 6, with the di/uo modes allowing efficient in-place updates without copying.[26]
Error handling in Mercury uses structured exceptions with try goals, catching specific terms or any via catch_any, while mode inference ensures type safety across branches. The example below wraps a potentially failing procedure p, catching string exceptions and rethrowing others, with I/O state threading through parameters.
:- pred p_carefully(io::di, io::uo) is det.
p_carefully(!IO) :-
try [io(!IO)] (
p(!IO)
)
then
true
else
io.write_string("p failed\n", !IO)
catch (S) ->
io.write_string("p threw: ", !IO),
io.write_string(S, !IO),
io.nl(!IO)
catch_any (Other) ->
throw(Other).
:- pred p_carefully(io::di, io::uo) is det.
p_carefully(!IO) :-
try [io(!IO)] (
p(!IO)
)
then
true
else
io.write_string("p failed\n", !IO)
catch (S) ->
io.write_string("p threw: ", !IO),
io.write_string(S, !IO),
io.nl(!IO)
catch_any (Other) ->
throw(Other).
det) overall, executing the then branch on success, else on failure, and appropriate catch on exceptions, preserving the program's declarative flow.[39]
Concurrency in Mercury supports impure threads for parallel execution or integration with backends like Erlang for actor-model concurrency. Using the thread module, one can spawn a new thread to perform a computation asynchronously. A basic example spawns a thread to compute the sum of a list and print it, without retrieving the result directly:
:- import_module thread.
:- import_module list.
:- import_module int.
:- pred concurrent_sum(list(int)::in, io::di, io::uo) is det.
concurrent_sum(L, !IO) :-
thread.spawn(
(pred(!IO) is cc_multi :-
sum_list(L, S),
io.write_int(S, !IO),
io.nl(!IO)
), !IO).
:- import_module thread.
:- import_module list.
:- import_module int.
:- pred concurrent_sum(list(int)::in, io::di, io::uo) is det.
concurrent_sum(L, !IO) :-
thread.spawn(
(pred(!IO) is cc_multi :-
sum_list(L, S),
io.write_int(S, !IO),
io.nl(!IO)
), !IO).
spawn predicate launches a concurrent closure with committed-choice multi determinism (cc_multi), performing the sum and output within the thread. For retrieving results, the thread.future module can be used. For Erlang backend usage, compile with --target erlang to leverage BEAM's lightweight processes for distributed concurrency.[40][8]
Real-World Applications
Mercury has been applied in production software, notably in the Prince XML formatter developed by YesLogic, which converts HTML and CSS to PDF. This tool benefits from Mercury's declarative approach for efficient tree processing and reliable large-scale development. The language's own compiler and tools are also implemented in Mercury, demonstrating its capability for self-hosting complex systems.[41]Release History
Major Versions
The development of Mercury began with early beta and stable releases in the 1990s and early 2000s, establishing its core as a declarative logic programming language with strong static analysis. Version 0.3, released on July 18, 1995, marked the first public beta release, introducing key features such as strong typing with parametric polymorphism, a mode system for input/output declarations, a determinism category system, and a module structure separating interfaces from implementations.[10] This version compiled to C for Unix platforms, emphasizing compile-time error detection to support large-scale, reliable software development.[42] Version 0.9, released in December 1999, represented the first stable release, incorporating enhancements like existential types, abstract instance declarations for type classes, user-defined infix operators, and an updated standard library with exception handling, time and garbage collection modules, and function-based predicates.[43] It also improved the debugger with interactive querying, command-line editing, and breakpoint support, while adding extras like lazy evaluation and POSIX bindings, though some changes affected backward compatibility.[44] A significant shift occurred with version 0.12, released on November 21, 2005, which improved the Java backend for compiling Mercury to Java bytecode, enabling better integration with Java ecosystems and improving foreign language interfacing.[45] Key additions included functional dependencies, new goal types, field access syntax, impure higher-order code, and constrained types, alongside library expansions for data structures like cords and 2D arrays, and enhanced Windows support.[8] The compiler saw optimizations such as optional type-accurate garbage collection and exception analysis, bolstering performance and debugging capabilities.[45] In 2010, version 10.04, released on August 30, adopted a new year-month naming convention (e.g., YY.MM) to reflect release timing, departing from the incremental 0.x scheme used for the first thirteen stable releases.[46] This version added pragmas for foreign enums and exports, trace goals, thread-local mutables, try-catch constructs, and higher-order instantiation support, while expanding the standard library with modules for parsing, graphs, and calendars, and refining the Java and Erlang backends.[8] Later major releases focused on refinement and portability. Version 14.01, released on February 10, 2014, permitted repeated type variables in type class instances, enhancing flexibility, and added numerous library predicates for data structures like cords, arrays, and maps, with improved support for modern compilers such as GCC 4.8 and Visual Studio 2013.[8] It included binary packages for Windows, aiding cross-platform adoption.[47] Version 14.04, released in April 2014, introduced advanced higher-order analysis for better optimization of functional constructs, contributing to more efficient code generation.[9] The 20.06 release in June 2020 included library updates and refinements to the Java backend for better ecosystem integration.[48] Throughout these versions, Mercury maintained a strong commitment to backward compatibility, with minor releases primarily addressing bug fixes, performance tweaks, and documentation updates to ensure stability for existing codebases.[25]Recent Developments
The latest stable release of Mercury is version 22.01.8, issued on September 17, 2023, as a bug-fix update to address issues identified in prior versions.[8] This release continues the project's emphasis on stability, with no major feature additions but refinements to existing functionality, including clarifications in documentation for output handling in the standard library.[5] In 2024, Mercury saw a notable enhancement to its compiler diagnostics, with colorized output enabled by default on June 24 to improve readability during development and debugging.[8] This update, documented in the Mercury Users' Guide, reflects ongoing efforts to refine user experience without introducing new language paradigms.[49] Maintenance remains steady into 2025, with no major releases since 2023 but regular Release of the Day (ROTD) builds available for testing emerging fixes and compatibility improvements. As of November 2025, the latest stable release remains version 22.01.8.[9] The project's GitHub repository stays active, hosting the source code and facilitating contributions from developers worldwide.[7] Community engagement persists through dedicated mailing lists, including those for announcements, user discussions, and development feedback, underscoring Mercury's commitment to reliability and practical usability over experimental features.[50]References
- https://www.wikidata.org/wiki/Q206040
