Next: , Previous: IO, Up: Top   [Contents]


4 Assert and retract

In Prolog, calls to the builtin predicates assert and retract can change the set of clauses of the program currently being executed. This makes compilation very tricky, and different Prolog systems react differently when the program alters the definition of a predicate that has active calls. It also makes program analysis almost impossible, since the program that the compiler should analyze is not actually available at compilation time. Since Mercury is a compiled language, it does not allow the compiled program to be altered in any way.

Most uses of assert and retract in Prolog programs are not actually intended to alter the program. Their purpose is just to maintain a set of facts, with semantically separate sets of facts being stored in separate predicates. (Most Prolog systems require these predicates to be marked as dynamic predicates.) A Mercury programmer who wants to store a set of facts would simply store those facts as data (not as code) in a data structure.

The standard library contains several abstract data types (ADTs) for storing collections of items, each of which is useful for different classes of problems.

If the order of the items in the collection is important, consider the list and cord ADTs. list has lower constant factors, but the cord ADTs supports concatenation in constant time. The stack and queue ADTs implement lists with specific semantics and operations appropriate to those semantics.

If the order of items in the collection is not important, and if the items are key-value pairs, you can store them in ADTs implementing several different kinds of trees, including rbtree and tree234. In the absence of a compelling reason to choose a different implementation, we recommend the map ADT for generic use. Maps are implemented using 234 trees, which are guaranteed to be balanced and thus have good worst-case behavior, but also have good performance in the average case. bimap, injection, multi_map and rtree are specialized version of maps.

If the items in the collection are not key-value pairs, then consider the set and bag ADTs. The set ADT itself has several versions, some based on trees and some based on bit vectors, each with its own tradeoffs.

The Mercury standard library has some modules for more specialized collections as well, such as graphs. And of course, if needed, you can always create your own ADT.

If for some reason you cannot thread variables holding some data through the parts of your program that need access to that data, then you can store that data in a ‘mutable’, which is as close as Mercury comes to Prolog’s dynamic predicates. Each Mercury mutable stores one value, though of course this value can be a collection, and that collection may be (but doesn’t have to be) implemented by one of the Mercury standard library modules listed above.

Each mutable has a getter and setter predicate. You can set things up so that the getter and setter predicates both function as I/O operations, destroying the current state of the world and returning a new state of the world. This effectively considers the mutable to be part of the state of the world outside the Mercury program.

Alternatively, you can set things up so that the getter and setter predicates of a mutable are not I/O operations, but in that case calls to those predicates are not considered pure Mercury, and must instead use Mercury’s mechanisms for controlled impurity. These mechanisms require all code that is not pure Mercury to be explicitly marked as such. They are intended to allow programmers to implement pure interfaces using small pieces of impure code, for use in circumstances where there is no feasible way to implement that same interface using pure code. Most Mercury programs do not use impure code at all. The ones that do make use of it use it very sparingly, with 99.9+% of their code being pure Mercury.


Next: , Previous: IO, Up: Top   [Contents]