Difference between revisions of "Functor Domain Versioning"

From wiki.visual-prolog.com

(initial)
 
m (category)
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{Template:NonReleased}}
+
It is problematic to update functor domains, if terms have been persisted in serialized form (e.g. using <vp>save</vp> to save the terms in a file).
  
It is problematic to update functor domains, if terms have been persisted in serialized form.  Because new programs cannot deal with old serializations.
+
The persisted terms will be in the old format, but the program expects the new format.
  
The attributes <vp>default/1</vp> and <vp>retiredFunctor/1</vp> can help dealing with this problem.
+
{{Example|A program have persisted terms of this domain to a text file:
 +
<vip>domains
 +
    ppp =
 +
        fct(
 +
            integer A,
 +
            real B,
 +
            string C
 +
        ).
 +
</vip>
 +
In the file the terms will look like this
 +
    fct(7,19.5,"something")
 +
Now we want to have an additional integer list in <vp>fct</vp>:
 +
<vip>domains
 +
    ppp =
 +
        fct(
 +
            integer A,
 +
            real B,
 +
            string C,
 +
            integer* NewInfo
 +
        ).
 +
</vip>
 +
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 <vp>default/1</vp> and <vp>retiredFunctor/1</vp> can help dealing with such problems.
  
 
== Default values ==
 
== Default values ==
  
The last arguments of a functor can have default values:
+
The '''last''' arguments of a functor can have default values:
  
 
<vip>domains
 
<vip>domains
Line 14: Line 40:
 
             integer A,
 
             integer A,
 
             real B,
 
             real B,
             ddd C,
+
             string C,
             integer* New [default([])],
+
             integer* NewInfo [default([])]
            real New2 [default(0)]
+
         ).
         ).</vip>
+
</vip>
  
 
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.
 
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.
+
'''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:
 +
<vip>T = toTerm(ppp, "fct(7,19.5,\"something\")")
 +
</vip>
 +
Will make this binding of <vp>T</vp>
 +
<vip>T =  fct(7, 19.5, "something", [])
 +
</vip>
 +
I.e. the default argument <vp>[]</vp> 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:
 +
<vip>clauses
 +
    q() = fct(7,19.5,"something").  % illegal: missing last argument
 +
</vip>}}
  
 
== Retired functors ==
 
== Retired functors ==
  
Functor alternatives can be retired.
+
Assume that we not only want to add an extra integer list to the <vp>fct</vp> functor, we also want to drop the <vp>real B</vp>.
  
 
<vip>domains
 
<vip>domains
 
     ppp =
 
     ppp =
         fct(integer A, real B, ddd C) [retiredFunctor(aaa::fct_ppp)];
+
         fct(
        fct2(integer A, ddd C, integer* New).</vip>
+
            integer A,
 +
            string C,
 +
            integer* NewInfo
 +
        ).
 +
</vip>
 +
 
 +
Now deserialization cannot be handled simply by adding a default argument to the last attributes.
  
<vp>aaa::fct_ppp</vp> must be a predicate with this type (it can be an anonymous predicate):
+
Instead the old functor alternative <vp>fct</vp> can be retired and replaced by a new <vp>fct2</vp>:
 +
 
 +
<vip>domains
 +
    ppp =
 +
        fct(integer A, real B, string C) [retiredFunctor(aaa::fct_ppp)];
 +
        fct2(integer A, string C, integer* New).
 +
</vip>
 +
 
 +
The <vp>retiredFunctor</vp> 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 <vp>aaa::fct_ppp</vp> will be used to correct the problem.  The predicate <vp>aaa::fct_ppp</vp> must have the type:
  
 
<vip>predicates
 
<vip>predicates
     fct_ppp : (integer A, real B, ddd C) -> ppp NewValue.</vip>
+
     fct_ppp : (integer A, real B, string C) -> ppp NewValue.
 +
</vip>
  
 
I.e. it takes the arguments of the old functor and returns a value in the functor domain.
 
I.e. it takes the arguments of the old functor and returns a value in the functor domain.
  
In the program <vp>fct</vp> is does not exist at all, it is retired.  However, in the deserialization <vp>fct</vp> terms can still be handled:  The arguments are parsed according to the types, and then the value is created by invoking <vp>aaa::fct_ppp</vp>.
+
We can implement fct_ppp like this:
  
This method will also work for binary serializations, provided:
+
<vip>clauses
 +
    fct_ppp(A, _B, C) = fct2(A, C, []).
 +
</vip>
 +
   
 +
{{Example|This code:
 +
<vip>T = toTerm(ppp, "fct(7,19.5,\"something\")")
 +
</vip>
 +
Will effectively correspond to this predicate call
 +
<vip>T =  aaa::fct_ppp(7, 19.5, "something")
 +
</vip>
 +
And will therefore result in this binding:
 +
<vip>T =  fct2(7, "something", [])
 +
</vip>
 +
}}
 +
 
 +
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:
 +
<vip>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
 +
</vip>
 +
}}
 +
 
 +
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)
 
* The old domain had more than one alternative (so there are functor numbers in the serialization)
Line 47: Line 131:
  
 
(It is however not recommend using binary serialization for inter-session persistence).
 
(It is however not recommend using binary serialization for inter-session persistence).
[[Category:Tutorials|{{PAGENAME}}]]
+
 
 +
[[Category:Data types and handling|{{PAGENAME}}]]

Latest revision as of 15:58, 19 February 2019

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).