Mercury programs closely resemble mathematically logic. Programmers write programs as a set of relations augmented with declarations that say how the programmer intends these relations to be used.
:- pred hello(string, postcode, string). :- mode hello(in, in, out) is semidet. hello(Name, PostCode, Greeting) :- suburb_codes(PostCode, Suburb), Greeting = "Hello " ++ Name ++ " of " ++ Suburb. :- pred suburb_codes(postcode, string). :- mode suburb_codes(in, out) is semidet. suburb_codes(2000, "Sydney"). suburb_codes(3000, "Melbourne").
A call to hello("Mary", 2000, X) will mean X = "Hello Mary of Sydney".
Mercury is a very expressive, high-level programming language. It automates several common programming tasks such as giving the types of local variables, allocating and deallocating memory, and caching previously computed results.
Mercury has a strong type system, and a mode system that does dataflow checking. These checks ensure that a large class of programming errors such as missing cases in switches and uninitialized variables are guaranteed to be caught by the compiler. It is a common experience to hear from Mercury programmers "Once I got my program past the compiler, it worked first time!" The compiler also simplifies maintenance tasks such as adding a new field to a structure by pointing out all the places where the code must be modified to accommodate the new field. Mercury's type system is very advanced, and provides powerful programming abstractions such as parametric polymorphism (similar to generics and templates), type-classes (similar to interfaces in Java) and user-controlled dynamic typing while still retaining complete safety. Type classes can be used to interoperate with component-based systems such as COM and CORBA.
Mercury has been used in a wide variety of applications, which include intelligent planning systems, a process flow web-server, formal specification animation systems and neural net implementations. It is well suited to creating complex, flexible, highly reliable applications. Some of these applications have already been deployed commercially.
In declarative programming languages programmers clearly and cleanly specify what is to be computed, and leaving the question of how to compute it to the compiler and runtime system. Programmers are relieved of the burden of allocating memory, specifying the order of operations (except where the order is visible in the output), or defining data-structures down to the bit level. The intention is to allow the programmer to devote their time to solving problems, rather than programming machines.
Since the burden of deciding how to implement the program has been shifted to the compiler, declarative language compilers are pretty smart. Declarative languages are simple and state directly what the result of the program is supposed to be; the compiler is free to re-arrange the program in any way it desires so long as the result of the program remains the same. Declarative language compilers routinely automate optimisations that are usually considered too difficult to implement in imperative language compilers, and too time consuming and error prone to do by hand.
The Mercury implementation can already optimise programs to remove intermediate data structures, omit unnecessary sub-computations and redundant tests, turn structure traversals into loops, perform cross-module inlining, eliminate unused arguments or procedures and specialize code to operate on specific types, and more optimisations are being added all the time. The upshot of all this optimisation is that programmers can write more complex programs more quickly, and still get competitive performance.
Another important benefit of the absence of side-effects is that one can use declarative debugging to debug programs written in declarative programming languages. With traditional debuggers, all of the grunt work of finding the bug has to be done by the programmer. Declarative debugging automates much of this grunt work. Declarative debuggers ask the programmer a series of questions about the intended behaviour of the program. By comparing the intended behaviour of the program with its actual behaviour, they can find the point in the program where the results of several correct sub-computations are combined into an incorrect result; in other words, they find a place where the program starts to go wrong. Having the debugger direct the search for the bug is particularly useful for novice programmers, who often don't know where to start. However it often helps experienced programmers as well, by preventing them from spending too much effort searching blind alleys. Searching blind alleys can be very time consuming as well as frustrating; avoiding such searches should make debugging more enjoyable, productive and predictable.
The .NET CLR has been designed with languages such as C++, C# and Visual Basic in mind. Therefore the language constructs that can appear in the interfaces between .NET components are limited to those found in imperative languages and their object-oriented extensions. Fortunately Microsoft realized that non-mainstream languages have strengths that some developers will want to exploit. Accordingly they decided to consult with the implementors of several emerging languages, both in academia and industry, on the best ways to extend the CLR to avoid such problems. Since such problems can only be found by experience with real implementations, Microsoft funded several research groups (including ours) to modify their language implementations to target the CLR and provide feedback and suggestions. For example, some features in Mercury are not represented directly in the CLR, and so cannot be exposed to other components (although they can of course continue to be used inside Mercury components). These kinds of mismatches provide suggestions for extensions of both Mercury and the CLR. Some of these suggestions have already been acted upon, and will be included in the first public release of the CLR, while others will be incorporated in future releases. Support for parametric polymorphism is likely to fall into the second category; it is already being prototyped by Microsoft Research.
We have completed a prototype backend in the Mercury implementation that generates code for the CLR. This code can interoperate with code generated by any other compiler on the .NET platform. If developers want to enjoy the benefits of Mercury, they can start small by writing a few .NET components in Mercury while the rest of the project is implemented in more traditional languages. The .NET component model allows a company to evaluate and roll out advanced programming languages without introducing critical project risks.
To demonstrate the possibilities of interoperation in this environment, we extended the Mercury implementation of eliza, a classic AI program that pretends to be a psychotherapist. Using the CLR COM interface, we used a standard COM object (Microsoft Agent) to give eliza a voice and an animated character. We used the WinForms class library in .NET and some C# code to give eliza a very simple graphical front end, where users could enter text and view eliza's previous responses. This shows the strength of multi-language development: unlike C#, Mercury has features that make writing an AI engine easier, whereas existing GUI builders generate C# code, not Mercury code. The .NET platform allows programmers to use the right tool for the right job.
Links:
The Mercury
homepage
Mercury and .NET
Written by Tyson Dowd on behalf of the Mercury team.