Functor Domain Versioning

From wiki.visual-prolog.com

It is problematic to update functor domains, if terms have been persisted in serialized form (e.g. using save to save the terms in a file).

The persisted terms will be in the old format, but the program expects the new format.

Example A program have persisted terms of this domain to a text file:
domains
    ppp =
        fct(
            integer A,
            real B,
            string C
        ).

In the file the terms will look like this

   fct(7,19.5,"something")

Now we want to have an additional integer list in fct:

domains
    ppp =
        fct(
            integer A,
            real B,
            string C,
            integer* NewInfo
        ).

However, a serialized version of such a term looks like this:

   fct(7,19.5,"something",[23,53,1])

And therefore we can no longer read our old terms; they lack a list.

The attributes default/1 and retiredFunctor/1 can help dealing with such problems.

Default values

The last arguments of a functor can have default values:

domains
    ppp =
        fct(
            integer A,
            real B,
            string C,
            integer* NewInfo [default([])]
        ).

The default attribute does not change anything within the program; it only affects the deserialization. If during deserialization we meet the closing parenthesis too soon we supply default values for the remaining arguments.

Notice that this will only work for text deserialization, in binary form there is no way to determine whether there are fewer arguments or not.

Example This code:
T = toTerm(ppp, "fct(7,19.5,\"something\")")

Will make this binding of T

T =  fct(7, 19.5, "something", [])

I.e. the default argument [] will be added to the term during the deserialization, to compensate for the missing term.

The default attribute does not have any effect for the code that must be written in the program, in that code the last argument must always be present.

Example This code is not legal:
clauses
    q() = fct(7,19.5,"something").  % illegal: missing last argument

Retired functors

Assume that we not only want to add an extra integer list to the fct functor, we also want to drop the real B.

domains
    ppp =
        fct(
            integer A,
            string C,
            integer* NewInfo
        ).

Now deserialization cannot be handled simply by adding a default argument to the last attributes.

Instead the old functor alternative fct can be retired and replaced by a new fct2:

domains
    ppp =
        fct(integer A, real B, string C) [retiredFunctor(aaa::fct_ppp)];
        fct2(integer A, string C, integer* New).

The retiredFunctor attribute says that this alternative is no longer in the domain; it can however still be expected in serialized terms. When met in a serialized term, the predicate aaa::fct_ppp will be used to correct the problem. The predicate aaa::fct_ppp must have the type:

predicates
    fct_ppp : (integer A, real B, string C) -> ppp NewValue.

I.e. it takes the arguments of the old functor and returns a value in the functor domain.

We can implement fct_ppp like this:

clauses
    fct_ppp(A, _B, C) = fct2(A, C, []).
Example This code:
T = toTerm(ppp, "fct(7,19.5,\"something\")")

Will effectively correspond to this predicate call

T =  aaa::fct_ppp(7, 19.5, "something")

And will therefore result in this binding:

T =  fct2(7, "something", [])

The retired functor does not exist inside the program; the program can only see the rest of the functors, i.e. the non-retired ones.

Example This means that you not only don't need to handle the old functor; it is indeed illegal:
predicates
    pred : (ppp P).
clauses
    pred(fct(A, B, C)) :- ... % illegal this functor does not exist it is retired
 
    pred(fct2(A, C, New)) :- ... % This is the only case in the code

retiredFunctor/1 will also work for binary serializations, provided:

  • The old domain had more than one alternative (so there are functor numbers in the serialization)
  • New alternatives are added last (to retain functor numbers)

(It is however not recommend using binary serialization for inter-session persistence).