In working with the language, I do get the impression that the static type checking is very tight. If you can get the code to compile, it's close to being a guarantee of correctness.
As with most of the languages I study, I used the shapes example to try to get a handle on the type and class mechanisms within the language (mercury version). I got some help from their mailing list along the way. The people on the list were quite helpful and seemed genuinely interested in sharing their appreciation for the language.
Posted to Logic/Declerative by Chris Rathman on 4/25/01; 11:49:26 AM
From what I've seen, Mercury's type system seems to be similar to Haskell's (there are often posts on the Haskell mailing list from Fergus H that compare the two).The type systems of Haskell, Clean and Mercury are fairly close to each in terms of usage, though the exact syntax is distinctive. Typeclasses are used to define the family of functions available for a type. Instances are used to map implementations. And records are used to build data structures (Clean & Mercury are closer in this respect - Haskell uses the data constructor - but the net effect is close).
At least from the polymorphism example I wrote, these three languages have very similar solutions. The one thing I did do for the Mercury example, that I didn't do for the others, was to use the existential typing mechanism. I know that Clean has existential types, but I couldn't figure it out at the time I was doing it. Haskell '98 doesn't come standard with existential types, but I think I read somewhere that Hugs has an extension. One of these days, I need to go back and fix the Clean and Haskell examples.
Is Haskell plus search minus laziness a useful description?Mercury doesn't support lazy evaluation. I would assume that the 'search' part is in reference to Mercury being solidly rooted in the Prolog goal directed evaluation?
Considering Haskell is a pure functional language and Mercury is a pure logic/declarative language, I figure it is impressive that we can describe them in terms of each other. :-)
I'm on dangerous ground here - I know nothing about logic programming - but I thought that it needed two things: a search (normally depth first + cuts) and some kind of variable unification process. I'm so hazy on what a "variable unification process" is that I didn't mention that bit...
I guess what I'm really asking is how much the goal-directed part comes into things. I get the impression that it's not a major part of Haskell (for example, it wasn't used in the programming contest entry, IIRC).
Oh, and I think existential types are now pretty common in Haskell (hugs + ghc at least?).
Everything in Haskell is a function. Everything in Mercury is a predicate. Functions in Mercury happen to be predicates with a deterministic quality - that is they have guaranteed unification and a single result.
There are some documents on the Mercury site that might be of interest in a comparison: Comparing Mercury with Haskell and The Prolog to Mercury transition guide.
Guess I'll have to get on a Haskell mailing list. :-)
Here's a short snippet, where the second function won't compile:
module Test
where
class Shape a where
getX :: a -> Int
setX :: a -> Int -> a
instance Shape ShapeExistential where
getX (MakeShapeExistential a) = getX a
setX (MakeShapeExistential a) newx = setX a newx
data ShapeExistential =
forall a. Shape a => MakeShapeExistential a
I went back and redid the Haskell shapes example and cleaned it up a bit. It now functions and operates almost identically to the Mercury shapes example.
(my own latest meddling in Haskell uses no classes at all, but wouldn't be possible (well, not quite as easy!) in an eager language)
The Functional and Logic programming languages like Haskell, Mercury, Erlang and Prolog don't have the notion of mutable variables like most standard OOP languages (which have their roots in the imperative world). But you can emulate property changes by effectively creating a new object any time a property is changed. (Erlang is kind of funky in this respect in that you can emulate mutable variables via thread messaging and tail-recursion - so the example, though wordy, does come closer to a mutable object).
The other problem with the Functional languages is that the classes are strictly organized around function signatures. In OOP languages, the classes are a conglomeration of properties (data) and methods. Haskell and Mercury have very high level concepts of inheritance built into their class mechanisms, but the inheritance only works on the level of function signatures. The methods don't really have an inheritance structure.
Now, it's possible to define the functions that operate on the base record (setX, moveTo, etc...) that use parametric polymorphism (templates) instead of relying on subtype polymorphism. Unfortunately, this goes beyond what I'm trying to do with the example. Using parametric polymorphism (really the norm in Haskell) would apply the setX function across all types in the same way. Whereas I'm trying to do the OOP trick of having setX be encapsulated within the Object.
The objects in Haskell would be a combination of the data record and the class (function signatures), using the module mechanism to encapsulate the behavior of the object.
One thing that might be worth noting is that the result is similar to Java interfaces. That is, emulating OOP in Functional languages is more akin to Java Interfaces than it is to subtype inheritance. Like Java interfaces, the interface definition just defines the signature and does not have the capability to instantiate a common method instance. If you used Java interfaces to solve the problem, you'd run into the same problem that setX would have to be defined in each implementation.
The comment about the OO words was triggered by the type constructors. Because these are used in pattern matching it looked a bit odd (to me)!