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.