So, I've been using the Mercury programming language for a little while now. I've kind of wanted to learn it for many years, but it's extremely different from any other language that I properly know, so until now I haven't really been able to even begin learning it.
I thought I would share what I've learned about it, because I am really happy that I am finally making progress learning it, and because I think it is a really nice language.
Mercury is a functional language, influenced by Haskell, but it's also heavily influenced by the logic language Prolog. All I really know about Haskell is that it is purely functional, declarative, and has a powerful type system. I don't really know much of anything about Prolog. That, and people say "Haskell is unrealistic about some things because it's a research language", and "Prolog is slow". I do know that Mercury has a very similar syntax to Prolog and a very similar type system to Haskell.
What you know about Prolog and Haskell is kind of important when learning Mercury. I found out very recently that almost everyone using it came over from Haskell or Prolog. Which explains why I had such a hard time even beginning to understand it--all the documentation assumes you know either Prolog or Haskell.
So, without knowing much about its roots, what do I find interesting about Mercury?
Mainly, how it allows you to express the state of a program. I think
this is a good example of the interplay of logic and functional programming:
% from a small directory searching routine
% This is a disjunction. The closest thing you could relate it to in imperative programming is a set of logical-or statements, or kind of a switch statement.
% PathType has been assigned, OutPath has not been assigned yet. Both are a 'maybe' type.
PathType = yes(directory),
OutPath = yes(InPath)
% After each semicolon is another possible state.
PathType = yes(file),
OutPath = yes(InPath)
PathType = no,
OutPath = no
In that case, PathType is a variable that has a state already. If it is equal to `yes(directory)' or `yes(path)', then the OutPath variable (which had not been assigned a value yet) is now set. Otherwise, this predicate (kind of the same thing as a function) fails.
With SLD resolution (which I'm told is inherited from Prolog), the idea of left-hand and right-hand expressions doesn't exist. Either a state is possible or its not. So what stops PathType from being reassigned? In Functional languages, a variable can only be assigned once. That's the combination of logical and functional programming: You describe the state, and if the state is possible based what values have been assigned, it becomes so. If two states are possible, but mutually exclusive, the compiler will know and your program won't compile.
To this end, there are many nice ways of expressing logical relations, such as the disjunction. While in that example it assigns to OutPath, the entire disjunction (everything in the parentheses) is itself a boolean expression. If any of the states it lists are possible, it is true. It's an expressive logical-or statement! There is a similar expression, a conjunction, which is the same as a logical-and statement. In fact, it's written using commas instead of semicolons. Which means that everything is either a huge logical-and statement, logical-or statement, or combination of the two. Your entire program can be said to be true or false, deterministic or non-deterministic. This means that it's simple to statically analyze the program and determine if a predicate has one solution, many solutions, or no solutions. Similarly, it's simple to see what predicates may fail.
Of course, there are a LOT of things I still need to learn. The determinism system in Mercury is much deeper than I understand right now, and I don't fully understand functional purity. But now that I'm actually making progress, and I understand some of the foundations of logic and functional programming, I'm rather enjoying learning Mercury.
But, perhaps there might be something to developing code in functional languages, I just worry about unnecessary verbosity and the time to completion ratio as well as the average bugs per hour one makes in it compared to other languages. Then in terms of 'correctness' are functional apps better in this area or fall into pitfalls too? I'm just not sure on that.
I suspect that Mercury code has fewer bugs than even Haskell or other functional languages do, since it is inspired by Prolog. Because of that the compiler has very advanced static analysis, and (my opinion) due to its largely declarative, predicate-based syntax I find it fairly easy to reason about what code will do.
I can't really say if it's more time consuming to write Mercury than other languages like C or not. For my project for Mercury--an X11 Window Manager that is compatible with the XSun server, instead of/in addition to X.Org--I can say that Mercury seems rather well grounded. It's not difficult to call into C, and there exist working backends for Java and C# that let you call into JVM code or CLI code, too.
Additionally, since it is first transcompiled to C (or C# or Java), it doesn't do some of the stranger things that Haskell or Prolog do (Haskell strings are linked lists of 32-bit UCS values? Lolwut?). It needs to be, at some point, API-interoperable with widely used languages. This probably helped prevent it from becoming too research-y.
Compared to my productivity in C for writing a WM, I'd rank it pretty similar even with how much work I have to do learning Mercury, although calling Xlib directly is some of the most obtuse C I've ever written, so it's probably a bad example for that.
I know exactly what you mean.