Hubbry Logo
Mercury (programming language)Mercury (programming language)Main
Open search
Mercury (programming language)
Community hub
Mercury (programming language)
logo
7 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Mercury (programming language)
Mercury (programming language)
from Wikipedia
Mercury
ParadigmLogic, functional, object-oriented[citation needed]
FamilyProlog, Haskell
Designed byZoltan Somogyi
DeveloperUniversity of Melbourne
First appearedApril 8, 1995; 30 years ago (1995-04-08)
Stable release
22.01.8[1] Edit this on Wikidata / 8 September 2023; 2 years ago (8 September 2023)
Typing disciplinestrong, static, polymorphic
Scopelexical
Implementation languageMercury
PlatformIA-32, x86-64, ARM, SPARC64, Java, CLI
OSCross-platform: Unix, Linux, macOS, Solaris, FreeBSD, OpenBSD, Windows, Android
LicenseGPL compiler,
LGPL standard library
Filename extensions.m
Websitewww.mercurylang.org
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[dubiousdiscuss]) 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]

Past

[edit]

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]

Hello World:

 :- 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]

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]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
Mercury is a purely declarative logic/functional programming language designed for creating large-scale, reliable, and efficient software applications, combining the expressiveness of declarative paradigms with advanced static analysis for error detection and optimization. Developed initially in the early by a team led by Zoltan Somogyi, including and , at the University of Melbourne's Department of , the language emerged from research aiming to address limitations in existing systems like by enabling programming-in-the-large. The first public descriptions of Mercury appeared in technical papers around 1994–1995, with the initial implementation focusing on a purely declarative model that avoids side effects and supports modularity through a robust module . Key features of Mercury include a strong static akin to that in , which enforces at ; mode analysis that tracks behavior of predicates to prevent runtime errors; and declarations specifying the number of solutions a predicate can produce, enabling precise control over nondeterminism. These mechanisms, along with higher-order programming support and for handling state via explicit modes (e.g., distinctions), allow developers to build complex systems while maintaining purity and efficiency comparable to imperative languages. Mercury's compiler generates highly optimized code, often outperforming traditional languages, and includes capabilities for declarative , automatic parallelization, and separate compilation to facilitate team-based development. The language remains actively maintained, with the latest stable release (version 22.01.8) issued in September 2023, and ongoing enhancements like improved compiler diagnostics.

Introduction

Overview

Mercury is a purely declarative language designed for the development of reliable, high-performance applications in real-world scenarios. It integrates the declarative expressiveness of , akin to , with elements, while prioritizing advanced static analysis to detect errors and enable optimizations at . This approach allows programmers to focus on what the program should accomplish rather than how, reducing common sources of bugs through rigorous checking mechanisms. The language's core goals emphasize modularity for large-scale , support for separate compilation to facilitate team development, and generation of efficient code that rivals the performance of imperative languages such as . 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. Mercury runs on a variety of platforms, including Unix-like systems such as and macOS, Windows, and Android via its Java backend. 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. Development of Mercury originated at the University of Melbourne's Department of and .

History

Mercury was initiated in October 1993 by a design team at the University of Melbourne's Department of and , aiming to create a language that addressed the shortcomings of existing systems like for large-scale, industrial-strength applications. The project was led by Zoltan Somogyi, with key contributions from and , who focused on incorporating strong static analysis to enhance error detection and performance. Development of the first began in December 1993, initially from NU-Prolog and later SICStus , with semantic analysis completed by May 1994 and code generation starting in August 1994. A significant milestone was achieved on February 24, 1995, when the successfully compiled itself, marking the transition to self-hosting and eliminating reliance on external systems. The stemmed from '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 in industrial contexts. 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. Subsequent releases in the late 1990s, such as version 0.7 in August 1997, added optimizations and interfaces, solidifying Mercury's maturity. 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. 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.

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 and support for styles through type classes and existential types. The foundational goals of Mercury address key weaknesses in , such as poor static error detection—where type errors often manifest only at runtime—and relatively slow execution speeds for large applications. By integrating strong static analysis, including types and modes, Mercury catches a substantial portion of errors during compilation, promoting reliability in industrial-scale software. In early benchmarks from the , it achieved up to 36 times the speed of bytecode-interpreted systems and nearly twice that of optimized native-code Prologs like Aquarius, approaching the efficiency of imperative languages such as or Ada through its generation of portable, optimized C code and compile-time garbage collection. Mercury prioritizes to foster clarity and expressiveness in complex, large-scale systems, where developers describe desired outcomes rather than step-by-step procedures. 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 overhead. 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. The design emphasizes to support programming in the large, with separate compilation per module and localized optimization decisions that enhance maintainability and scalability in team-based development. Modes contribute to these objectives by refining at , as explored further in the Mode and Determinism Analysis section.

Type System

Mercury features a strong, static, polymorphic based on many-sorted logic, which ensures at while supporting expressive programming constructs. This system includes higher-order types, allowing functions and predicates to be treated as first-class values, such as pred(int, [string](/page/String)) for a predicate taking an and , or func(T) = U for a polymorphic function type. 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). 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). 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. 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. 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. Discriminated unions, akin to algebraic data types, are defined with multiple constructors using the --> , 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). For , 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. These features collectively reduce runtime errors by enforcing type correctness statically and facilitate compiler optimizations, such as , by providing precise type information that, in conjunction with other analyses, identifies unused code paths.

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 to perform precise checks and optimizations at . 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 include in 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 , and uo (destructively output) for unique arguments produced as a result. These declarations allow the to track dependencies and reorder goals in conjunctions to satisfy mode constraints, ensuring that no argument is used before it is sufficiently instantiated. The system also incorporates advanced modes to manage mutability and . Instability modes, such as mostly_unique, track whether an argument's state changes can be safely reversed on , 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 detects errors such as attempts to read uninitialized variables or create unintended aliases, rejecting ill-moded programs before runtime. 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.
CategoryDescriptionSolutionsCan Fail?
detSucceeds exactly once without failure.Exactly 1No
semidetSucceeds at most once, possibly failing.0 or 1Yes
multiSucceeds more than once without failure.>1No
nondetSucceeds zero or more times, possibly failing.≥0Yes
cc_multiCommitted choice; succeeds more than once without failure.>1No
cc_nondetCommitted choice; succeeds zero or more times, possibly failing.≥0Yes
erroneousNever succeeds and may throw an exception or loop indefinitely.0N/A (aborts)
failureAlways fails without exceptions or loops.0Yes
These categories are declared with annotations like is det and influence ; for example, det goals can be executed in loops without overhead, while nondet goals require search mechanisms. During compilation, mode and are tightly integrated: the 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 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 , as detailed in the Mercury 's algorithms, ensures scalable across modules while handling higher-order code. 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 via uniqueness and ensuring mode-correct data flow, Mercury achieves high efficiency comparable to imperative languages while retaining declarative purity.

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 but allowing observation of hidden state. Impure code supports full side effects, including , I/O operations that change state, or calls to non-declarative foreign procedures, but requires explicit handling to prevent unintended propagation. The compiler enforces purity through explicit annotations on predicates and functions, using keywords like impure 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. Mercury's module system facilitates large-scale development by separating interfaces from implementations, promoting separate compilation and . 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 while allowing the to generate optimized independently for each module. Submodules, either nested or separate, extend this organization for hierarchical designs. 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.

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. Parsing begins with the scanner and parser modules, which convert Mercury syntax in .m files into an 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 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. In the middle-end, the applies high-level transformations to the HLDS, including higher-order specialization, which generates specialized versions of predicates when higher-order arguments are known at , and , which eliminates unnecessary intermediate data structures to reduce allocation and improve runtime . 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 information to prune non-viable execution paths and prevent unnecessary retries. These transformations produce an optimized HLDS, serving as the primary before translation to lower-level forms. 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.

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. 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. 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. 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 with the JVM and access to Java's extensive libraries, though it currently lacks built-in profiling support. Similarly, the C# backend targets the by generating C# code, allowing for C# and , with compatibility for both .NET and Mono runtimes, but sharing the limitation of no native profiling. The Erlang backend compiles a subset of Mercury to Erlang code, enabling execution on the Erlang to leverage its strengths in concurrency and fault-tolerant distributed systems, with good support for interfacing between Mercury and Erlang modules. 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. 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. 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. Bytecode interpreters were considered for rapid prototyping but were ultimately discontinued due to performance limitations in production environments. 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. 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.

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. 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. The primary mechanisms for embedding foreign code are pragmas such as foreign_proc, foreign_code, and foreign_decl. The pragma foreign_proc allows a Mercury predicate or function to be implemented directly in a , 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;"). 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>"). Similarly, pragma foreign_decl adds declarations, such as external function prototypes, to the interface file: :- pragma foreign_decl("C", "extern int my_function(int);"). Additional pragmas like foreign_export enable Mercury procedures to be callable from foreign code by specifying an export name. Mercury supports interfaces to several languages, with C offering the most mature implementation, followed by Java, C# (via Intermediate Language or IL), and Erlang. 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);"). In C#, types map to .NET equivalents, such as System.Int32 for integers. These interfaces require foreign procedures to be marked as impure or semipure, as they may perform side effects outside Mercury's purity system. 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; may be represented as head-tail structures with macros like MR_list_head. Trailing mechanisms, such as MR_trail_value, ensure updates are reversible during , though this adds overhead. For arrays or enums, pragmas like foreign_type and foreign_enum define custom mappings to maintain compatibility. Common use cases include accessing system calls, integrating with GUI toolkits like via C bindings, or utilizing numerical libraries such as BLAS without native Mercury wrappers. This allows Mercury programs to interface with vast ecosystems of foreign libraries, enhancing practicality for real-world applications. Limitations arise from the loss of Mercury's static analyses in foreign code, including type checking, mode analysis, and determinism guarantees across boundaries. Developers must manually handle , error propagation, and exceptions, as no automatic support exists for throwing across the C interface, for instance. Multi- and nondeterministic foreign procedures are unsupported, requiring fallback to Mercury clauses, and garbage collection does not manage trail entries, potentially leading to leaks.

Tools and Ecosystem

Development Tools

The primary tool for compiling Mercury programs is the mmc command, which serves as the front-end driver. It handles the translation of Mercury 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. support is activated with the --debug option, which instruments the code for use with the Mercury 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), , and C#, allowing developers to choose based on deployment needs. 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 and linker flags, including support for profiling via GRADEFLAGS=--profiling. This ensures efficient incremental builds while handling Mercury-specific features like interface files. 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, , or fails), next for skipping into subgoals, continue to resume until a , 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 . 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. Mercury includes support for through the require module in its , which provides assertion-like predicates such as expect/3 (which aborts with an error message if a fails) and require/2 (similarly enforcing 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 . While not a full-fledged framework, this enables straightforward unit tests within Mercury modules. 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 with an existing Mercury installation and are built using a , with options like --install-prefix <dir> for the installation directory and --libgrade <grade> for platform-specific grades (e.g., or windows). Platform configuration adjusts for operating systems and architectures via environment variables or the --env-type flag, ensuring compatibility across systems, Windows, and others. Full setup details are in the bootstrap .

Editor and IDE Support

Mercury offers editor support primarily through developer-provided and community-maintained plugins, emphasizing , indentation, and basic integration with the Mercury (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 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 , proper indentation, and keybindings such as C-c C-c for compiling the current file via mmc --make and C-c C-r for compiling and running without interactive input. The Flycheck library extends this with on-the-fly syntax checking and error display during editing. Additionally, Mercury's official documentation describes an interface for the debugger, which displays source code in a buffer and highlights the currently executing line as tracing progresses. 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. 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. For other text editors, has community-driven extensions like vscode-mercury, which delivers essential for Mercury keywords, modules, and predicates to improve code readability. The mercury-ls language server further enables features such as diagnostics, hover information, and error navigation by integrating with the Mercury compiler's output. supports Mercury through a TextMate-compatible grammar, enabling via community packages. The Atom editor also has an official Mercury package for similar basic editing features, though Atom's development has ceased as of 2022. IDE support for Mercury is limited to older plugins for and , which offer project creation, single-file compilation, , and integration with compiler errors for navigation. The plugin, for instance, targets versions 6.9 through 8.2 across Windows, , and macOS, functioning as part of the broader Mercury Development Environment for streamlined workflows. These tools lack the advanced refactoring, , or autocompletion found in contemporary IDEs for languages like . Community repositories on , active into 2024, host these and other extensions, filling gaps through volunteer contributions. No official plugins exist for , leaving developers to adapt general tools or generic language servers.

Standard Library

The Mercury standard library comprises over 115 modules, organized into categories based on functionality such as data structures, operations, , string and numeric manipulations, and utilities. 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. Core data structure modules include list 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. 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. The getopt module supports command-line option parsing, enabling programs to process arguments with short and long options. Additionally, the exception module handles error propagation and recovery through predicates for throwing and catching exceptions. 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. The require module facilitates assertions and conditional compilation via predicates like error/1 for runtime checks and unexpected/2 for handling invalid states. 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). 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. 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.

Examples and Applications

Basic Syntax Examples

Mercury's syntax draws from logic programming languages like 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. 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.

mercury

:- 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).

This program can be compiled using the Mercury compiler command mmc --make hello, which processes the file hello.m and generates an executable. 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:

mercury

:- 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.

Here, X and Y are inputs, and Z binds to their sum. This illustrates Mercury's use of unification (=) for assignment and computation. List processing in Mercury leverages its roots for search-based operations. The member predicate exemplifies nondeterministic search, succeeding multiple times if the element appears in the list:

mercury

:- 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).

This recursive definition binds X to each element in the input , demonstrating on list cons cells ([Head | Tail]) and for multiple solutions. 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:

mercury

:- 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).

This separation promotes modularity and separate compilation.

Advanced Usage Examples

Mercury's advanced features enable efficient of complex algorithms through declarative constructs. One common optimization leverages for recursive computations, such as calculating 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 number, utilizing higher-order aspects implicitly through the language's mode system for input-output behavior.

mercury

:- 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.

This declaration ensures that each value is computed once and reused, achieving O(n time for the nth term due to the tabled storage of results. For performance-critical sections involving low-level operations, Mercury allows inline foreign procedures, such as embedding code for mathematical functions like sine computation. This integrates seamlessly with Mercury's via mode annotations, promising purity to maintain declarative semantics.

mercury

:- 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);").

The pragma specifies C as the target language, with input mode 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. Module interactions highlight Mercury's , importing components like the list module for higher-order functions such as foldl. Consider summing elements in a of integers, where foldl applies an accumulator predicate left-to-right, demonstrating mode for destructive input-output updates.

mercury

:- 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).

Here, the lambda-like predicate (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. 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.

mercury

:- 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).

This construct maintains determinism (det) overall, executing the then branch on success, else on failure, and appropriate catch on exceptions, preserving the program's declarative flow. 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:

mercury

:- 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).

The 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.

Real-World Applications

Mercury has been applied in production software, notably in the Prince XML formatter developed by YesLogic, which converts 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.

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. This version compiled to C for Unix platforms, emphasizing compile-time error detection to support large-scale, reliable software development. 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 with , time and garbage collection modules, and function-based predicates. It also improved the with interactive querying, command-line editing, and support, while adding extras like and bindings, though some changes affected backward compatibility. 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. 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. The compiler saw optimizations such as optional type-accurate garbage collection and exception analysis, bolstering performance and debugging capabilities. In 2010, version 10.04, released on August 30, adopted a new year-month (e.g., YY.MM) to reflect release timing, departing from the incremental 0.x scheme used for the first thirteen stable releases. 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 with modules for parsing, graphs, and calendars, and refining the and Erlang backends. Later major releases focused on refinement and portability. Version 14.01, released on February 10, 2014, permitted repeated type variables in 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 2013. It included binary packages for Windows, aiding cross-platform adoption. Version 14.04, released in April 2014, introduced advanced higher-order analysis for better optimization of functional constructs, contributing to more efficient code generation. The 20.06 release in June 2020 included library updates and refinements to the backend for better ecosystem integration. Throughout these versions, Mercury maintained a strong commitment to , with minor releases primarily addressing bug fixes, performance tweaks, and updates to ensure stability for existing codebases.

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. This release continues the project's emphasis on stability, with no major feature additions but refinements to existing functionality, including clarifications in for output handling in the . In 2024, Mercury saw a notable enhancement to its diagnostics, with colorized output enabled by default on June 24 to improve readability during development and . This update, documented in the Mercury Users' Guide, reflects ongoing efforts to refine user experience without introducing new language paradigms. 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. The project's repository stays active, hosting the source code and facilitating contributions from developers worldwide. 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.

References

  1. https://www.wikidata.org/wiki/Q206040
Add your contribution
Related Hubs
User Avatar
No comments yet.