Closed paulch42 closed 7 years ago
PJ, your first "ordering not being inherited" example was just a bug. That is now fixed I think, so both useTn functions return true (because 1>2 for this type!). However, in fixing this, you bang up against what you should do if both types have ordering clauses. Currently, I'm using "overriding" semantics - so if you define a type with an order and then define a second type (using the first) with a different order, the different order wins when you are talking about the second type.
Presumably we should use an order on a union to override any orders from the members. But if there is no override, then I can't see what we can sensibly do with the orders from the subtypes that may conflict. Is this just a TC error?
Let me know if you want a jar update.
I think the "overriding" approach is the most reasonable approach . If we take that approach then I can't think of any particularly complex cases that we need to address.
I would like you to upload a new JAR file whenever you have time -- thanks!
OK, here's the latest. This includes a fix for Paul, who's also testing.
I can think of a case involving union types that puzzles me -- see below.
types
N = nat
eq t1 = t2 == t1 <> t2;
T = nat
eq t1 = t2 == t1 = t2;
functions
run: () -> bool
run() ==
let x : N | T = 1,
y : N | T = 2
in
x = y;
So what is x = y
suppose to do? Either the interpreter uses the equality definition of N
or T
or that of nat
. For some reason, it uses the equality definition of N
. Thoughts?
λ java -jar vdmj-4.0.0-170427.jar -vdmsl test.vdmsl -i
Parsed 1 module in 0.132 secs. No syntax errors
Type checked 1 module in 0.25 secs. No type errors
Initialized 1 module in 0.137 secs.
Interpreter started
> p run()
= true
Executed in 0.009 secs.
I'll debug and find out what's happening here.
OK, I see what's happening. It's to do with how types are assigned to values. When you say x:N | T, you're saying that that the value is either of type N or T. When the assignment happens, it attempts to find out which type in the union the value actually belongs to, and finds N first. So both x and y are of type N. The equality is then true (since = is defined as <>).
If you introduce type U = N | T, and then declare types of that named union U, it regards both variables to be of type U, which has no equality defined. You therefore get the default behaviour as things stand, which says that the simple numeric equality is false.
I think the problem ought to be raised when U = N | T is type checked, though I'm not sure what it should really say. A warning that the eqs will be ignored? An error because there are eqs? Or should the union try to inherit eqs from the members? How??
Thanks for the explanation. I would prefer raising an error because there are multiple "eqs" defined. It's non-sense anyway :) can't think of a situation where this would be useful.
I think I agree, and the same for ordering clauses. Though we have to be careful not to prevent people from composing unions from several types (that they did not write), in the case where they do not care about equality or ordering - though equality is very common, so perhaps that's nonsense. But you see my concern? If we make unions of types with eq/ord illegal, then it severely limits what you can do with such types.
You can't even really say that the eq/ord must all be "the same". It's very difficult even to test that all eqs are the same in the union - in general it amounts to comparing arbitrary typed expressions for equivalence.
Perhaps we should be less severe, go for a warning ("eq/ord will be ignored in union members"), and then allow the union type itself to provide eq/ord semantics, defaulting to the standard behaviour. The eq/ord expressions can then use "is_" type tests to decide what to do with the elements.
Maybe we should automatically compose implicit eq_N|T
and ord_N|T
(we won't need these nonsense names though) to apply in x = y
and x < y
where x, y : N|T
.
It would be something like
lambda x, y : N|T &
if is_(x, N) and is_(y, N) then eq_N(x, y)
elseif is_(x, T) and is_(y, T) then eq_T(x, y)
else x = y -- the default equality
and
lambda x, y : N|T &
if is_(x, N) and is_(y, N) then ord_N(x, y)
elseif is_(x, T) and is_(y, T) then ord_T(x, y)
else error <TYPE_MISMATCH>
What do you think?
I mean, operator overloading should be resolved at compilation time based on static types, not dynamic types. So, we will need to automatically compose anonymous eq/ord functions to resolve the eq/ord operators for anonymous union types (and maybe for anonymous option types).
You might be on to something here, Tomo. I think we're talking about whether the orderings were defining on the types are partial or total. When you add an "ord" clause to a type, you're effectively giving it a total order (because the rule applies to all pairs of that type, and one assumes the order relation would satisfy the other aspects of a total order). But when we combine types that have total orders into a union, you get a partial order or perhaps a more complex total order, depending on what the member types have.
I'm not sure that this helps, but it is a way of looking at what happens when you create a union. But what should the semantics of comparison be in that case?
The problem with composing an anonymous function, like Tomo suggests, is that it is the order of the if-elseif
clauses that determine which (say) eq
function that might get called, right?
I think that would be OK PJ. A value in the union can only be of subtype N or T?
Sorry for not being clear about this. My point is that it's not clear to the user which "eq" function is being called. Really it could be any of them? I mean both is_(x, N) and is_(y, N)
(the if
-clause) and is_(x, T) and is_(y, T)
(the elseif
clause) match.
You are right, PJ. We need to prioritize component types.
The order of appearance in the union definition, e.g. N
then T
for N | T
, might be a natural choice.
(coincidentally or not, it's the same order as Nick's implementation :-)
In order to compose a total order, may be we can wildly say like
lambda x, y : N|T &
if is_(x, N) and is_(y, N) then ord_N(x, y)
elseif is_(x, T) and is_(y, T) then ord_T(x, y)
else is_(x, N) -- N's value precedes T's when they belong to different types
I know this is rough and naive, but is one possible stuffing to make it total.
By the way, the default equality can't be true when the two values have no common type. So, the composed eq function would be like
lambda x, y : N|T &
if is_(x, N) and is_(y, N) then eq_N(x, y)
elseif is_(x, T) and is_(y, T) then eq_T(x, y)
else false
Assuming that we allow union types that define order and equality (which I think we need to do in order not to limit the usage of this feature too much, ref Nick's comment) then I guess the order of appearance is a natural choice. However, I'm not sure .. else is_(x, N)
is the best choice when defining order. Can't we raise a runtime error instead for this particular case?
I think we have the following choices for the last else
clause of ord
.
else undefined
-- runtime error or exceptionelse false
-- always false if no common type, for any comparison (<, <=, >, >=)else is_(x, N)
-- N's values precede T's valuesThe first one is pessimistic. It makes a partial order.
The second one looks natural to me, but violates the law of total order: a < b ⇔ not(b ≤ a)
.
The third one is tricky. It satisfies a < b ⇔ not(b ≤ a)
, but may break transitivity.
I'm a bit worried about the second and third cases because they define orderings that are not intuitive. Like you said, this leads to violation of certain properties that you by intuition would expect to hold. Does it severely limit the usage of this feature to make the order underfined
(case 1)? Other views on this?
I think the primary use of these clauses would be for composite types so I don't see a great limitation with respect to unions. All the examples I had come across that prompted the RM were composite types.
One thing I am not clear on is the discussion concerning automatically creating these ord functions. Don't we explicitly define the ord functions in the ord clause so if I want to say treat elements of N before elements of T in N|T then I must specify that in my ord clause, it is not implicit (the 'is_' expressions would appear in my ord clause).
It's avoiding the problem to some extent, but make progress with this we could insist that if you compose a union of types where at least one of them defines an ord clause, then we insist that you define an order for the union explicitly. That would override the member orderings. That order could then be any of the possibilities we are discussing, but it puts the ball back in the specifier's court and is far more explicit (which is usually a good thing).
I have been migrating the ISO8601 module to use eq/ord clauses as it is an example that significantly benefits from the new features. An issue has cropped up in the supporting Seq module. There exists a function
ascending[@a]: (@a * @a +> bool) +> seq of @a +> bool
ascending(o)(s) == forall i in set {1,...,len s - 1} & s(i) = s(i+1) or o(s(i), s(i+1));
that determines if a sequence is ascending with respect to an order relation. I tried to redefine in the new context as
ascending[@a]: seq of @a +> bool
ascending(s) == forall i in set {1,...,len s - 1} & s(i) <= s(i+1);
Rather than pass around an order relation it uses the relation defined in the ord
clause. This results in the error
Error 3139: Left hand of <= is not ordered in 'DEFAULT' (Ord.vdmsl) at line 5:60
Actual: @a
Error 3140: Right hand of <= is not ordered in 'DEFAULT' (Ord.vdmsl) at line 5:60
Actual: @a
I understand this, it is saying @a
is an arbitrary type so we don't know if it has an order relation. Unfortunately that means we can't write generic functions, other examples being descending
, insert
, and sort
, without passing round relations explicitly. What we really want to do here is add a precondition to the function that says "type @a has an ord clause defined", but such is not expressible (though it is similar in nature to expressions of the form is_(v,T)
).
At present the type checker fails because it rejects a<b
it it can't guarantee a
and b
are of a type that has an ord clause. Could this be weakened to a<b
is rejected if the type checker can guarantee a
and b
are of a type that does not have an ord clause. In the case above the second definition of ascending would be valid but fail at runtime if passed values of a type that does not have an ord clause.
Re Nick's comment, in general it is good to be more explicit, but I am not so sure in this instance. It seems overbearing to force me to define an order relation on a union type if I have no interest in ordering the elements of that type. I will just end up creating some arbitrary order relation to get round a technicality of the system, such as ord x < y == false
.
Fair point. Then how about we say that the ordering of a union is undefined (unless explicitly defined) and if you attempt to compare union members without having defined an order, you get a runtime exception? That way, if you're not interested there is no problem; if you are interested but forget to define it, you get a sharp reminder :-)
That sounds a reasonable approach. In some way a little related to what I was saying in the comment concerning the ascending function.
I think a type checker should raise a type error when it failed to find ordering on the static types of the both hands of a comparison operator.
I agree that explicit and conservative way is good.
T | U
does not have ordering.types V = T | U ord ...
has its own ordering.An explicit definition of ordering function would be complicated; it would require combinations of type discriminations of the member types. For convenience, maybe we can consider something like deriving clause in Haskell, like
types V = T | U deriving ord
automatically composes a partial ordering function from T and U's ordering.
This is explicit at some degree, and not tedious for specifiers.
What do you think?Nick: Fair point. Then how about we say that the ordering of a union is undefined (unless explicitly defined) and if you attempt to compare union members without having defined an order, you get a runtime exception? That way, if you're not interested there is no problem; if you are interested but forget to define it, you get a sharp reminder :-)
Yes, I would be OK with that.
I have migrated the ISO8601 example and associated modules to employ eq and ord clauses. The result is attached. A good test of the implemenation since it features eq and ord clauses on a number of types with three levels of nesting. It compiles and type checks, though I have not as yet run any tests.
Some comments relating to the benefits accrued:
The original module was about 800 lines, the revised one is around 700 lines. While not a particular goal, the reduced number of lines is a positive side effect, largely due to all the functions that need not be defined (this module is eq/ord dense so others would not see such a great benefit).
The co-location of the eq and ord clauses with the type definition and invariant does to my mind provide a significant benefit. The presentation of those relations with the type definition aids understanding of the type that is being specified, focussing on the key aspects in one location.
The infix operators (<, <=, >, >=) are used at many points when specifying the properties of functions that are defined subsequently. I find the familiarity of the infix operators together with a more concise presentation to provide a benefit in terms of understanding the specification.
So firstly, regarding unions, it turns out we can't naively do as I was suggesting - silently treating unions as unordered - because there are lots of existing tests in the language test suite that legitimately create unions of ordered and non-ordered types, and then make comparisons of the members (which are carefully arranged to be ordered only, of course). So we can't easily change union ordering semantics without potentially changing the meaning of existing specifications. So for now, I've left the test for union ordering as being true if any of the member types are ordered (or if you define a type for the union with an ord clause). There should be a runtime error if you end up comparing incomparable values, and a proof obligation should cover that.
Regarding the polymorphic function point... yes, this is tricky. For now I've relaxed the ordering assumption on polymorphic types, so that they are assumed to be ordered. That should allow us to do more testing, but the essential problem remains. We could perhaps introduce is_ord(val), meaning a symbol is an ordered type? And again, we will get a runtime error (and need a proof obligation) if the values are not ordered.
I'll do a few more tests with what I have, then I'll attach an updated jar.
OK, try the following:
Nick, thank you for the update. In the following spec,
types
OneTwoThree = <ONE> | <TWO> | <THREE> ord x < y == x <> y and cases mk_(x, y):
mk_(<ONE>, -) -> true,
mk_(<TWO>, <THREE>) -> true,
others -> false
end;
OneTwo = <ONE> | <TWO>;
values
unionOne:OneTwoThree|OneTwo = <ONE>;
unionTwo:OneTwoThree = <TWO>;
unionThree:OneTwoThree = <THREE>;
unionOne < unionTwo
causes a runtime error, which means vdmj failed to find the common static type <OneTwoThree>
.
Tomo, Stepping through that with a debugger, what happens is that at runtime, the test expression's left hand side is considered to be of type OneTwo. This is because you've declared that the type is either OneTwoThree or OneTwo, and at runtime the value is arbitrarily matched to one type or the other (since it is a member of both).
I don't know the correct semantics here. But I think this is similar to an example PJ had earlier. If you say that a static type is A | B, the type checker will understand the union, but at runtime any value you assign will be either A or B, and thereafter behave as an A or a B. It can't delay that decision until later and flip its identity to one of the other alternatives. It is different if you declare a new type C = A | B; in that case all value assignments behave like type C (which may have an invariant).
Have you tried similar experiments with VDMTools?
Nick, that's why I believe an overloaded operators should be resolved at compile type by a type checker. Because a VDM value can be of multiple types, one dynamic type is not enough to choose the right body of the function. Resolved at compilation time, the ord should be equivalent to
functions
order : OneTwoThree * OneTwoThree -> bool
order(x, y) == x <> y and cases mk_(x, y):
mk_(<ONE>, -) -> true,
mk_(<TWO>, <THREE>) -> true,
others -> false
end
and order(unionOne, unionTwo)
evaluates true
.
Well, I see your point Tomo. But unless you think this is a bug, we have to be careful about changing more fundamental aspects of the language semantics. What does VDMTools do in a similar situation?
The eq/ord clauses are the first "overloading function" feature in the VDM world at the language level. So, Nick's VDMJ preview is the only interpreter that deals with overloaded operators and thus there's no "similar situation".
The reason why I insist resolving at compile time is to limit the impact on the language semantics. By resolving at compile time, we can have an equivalent VDM spec without overloading by translating from a spec with eq/ord clauses. Any occurrences of comparison operators should be replaced with eq_T, ord_T, the built-in operators and their compound expressions. That'll ensure that we are NOT changing the foundation of the language semantics.
Well, how about invariant functions? If I change your example to the following:
types
OneTwoThree = <ONE> | <TWO> | <THREE> inv x == cases x:
<ONE> -> true,
others -> false
end;
OneTwo = <ONE> | <TWO>;
values
unionTwo: OneTwoThree | OneTwo = <TWO>;
Both VDMTools and VDMJ are happy to create the unionTwo value, even though it violates the invariant for the OneTwoThree type. I think that's because at runtime, the value is only one type or the other, not both. If more than one type in the union matches a value, then the value is assigned one of the matching types arbitrarily (at least in VDMJ; I don't have the VDMTools source).
You would expect this to be an error, because one of the types in the union will not admit the value?
Actually, looking at the VDMJ implementation, values that are converted to a union type are converted to the first type of the union (in an arbitrary order) that accepts the value. If no member type accepts the value, then obviously the union assignment is invalid. So in the case above, the OneTwo type accepts the value.
Nick, I DO agree that the unionTwo: OneTwoThree | OneTwo = <TWO>
is well typed.
It is simply because unionTwo
is not a value of the OneTwoThree
type as is_(unionTwo, OneTwoThree)
evaluates false
.
However, in the case of resolving the overloaded <
operator in unionOne < unionTwo
, the first type that accepts the unionOne
is not enough. All types of unionOne
and also all the types of unionTwo
matters. The static type, namely OneTwoThree|OneTwo
is the important clue to get all candidate types that carry with ord/eq clauses.
I've asked PGL to comment on this.
I'm worried that, in general, you're suggesting that unions form a sort of "behavioural union" whose members act according to the combined behaviour of their member types. To me, a union just describes a collection of values, each of which come from one of a set of types, and whose individual behaviour is determined by that one type. The runtime assignment an untyped value to a union chooses the type of that value (albeit arbitrarily, if several match, which I agree may be an issue).
If there was a union of class types, and several of those classes defined "compareTo" and "equals" functions, surely they would act independently? Isn't that the VDM++ equivalent of what is happening here with ord/eq?
Well I have been thinking about this and in general my view is that union types gives raise to challenges for many different aspects of tool analysis. If I should design a VDM-like language with the knowledge I have today I am sure that I would limit the way unions could be used. So if you look for example at RSL they essentially only have unions of records. If one was having just that all these analysis challenges would go away. I think that this may be slightly to limited since I would like for example also to make unions of disjoint basic types (e.g. bool ı nat). Thus, I would like to suggest that when we make changes to VDM in a VDM-10 setting we target to make it easier to use "good practice". What I recommend all my students is to only make unions of either records or disjoint basic types. Thus, I think that we should say that if users haven't followed this principle the "compareTo" and "equals" is simply undefined! It makes it simpler from a tool development perspective and we strongly encourage more VDM users to follow good modelling principles.
I agree. I think we already demonstrated that the combination of non-disjoint union and overloading is really complex. How about raising a type error when
I don't think this would harm existing specifications without eq/ord nor Paul's ISO8601 spec.
P.S. Nick,
If there was a union of class types, and several of those classes defined "compareTo" and "equals" functions, surely they would act independently? Isn't that the VDM++ equivalent of what is happening here with ord/eq?
Interesting. Functions are static members of classes that should be resolved by static types. So, for this case, finding the first matching type from members of union type and get the static method is the right behavior of the interpreter. The difference is that a static method is resolved by only the static type of the object value, while an overloaded operator is resolved by the entire signature (the static types of the both arguments because the return type is always bool in the case of ord/eq operators). Comparison operators needs the possible types of the both arguments to find out the right definition with a matched signature.
Interesting comments on union types. As it happens I had some issues in this direction when creating a specification for an aviation domain. The problem was I wanted to take the union of two types, both based on seq of char
, which were distinct but not disjoint. A simple example
A = seq of char
inv a == len a < 4;
B = seq of char
inv b == len b > 2;
so any three character sequence satisfies both is_A
and is_B
. At first I started out changing to
A1:: a:seq of char
inv a == len a.a < 4;
B1:: b:seq of char
inv b == len b.b > 2;
effectively making them disjoint. However that made my specifications awkward because it was messy to have to handle the field tags when I was using A1
or B1
independently (which was the majority of uses).
What I ended up doing was sticking with the simpler definitions of A
and B
but instead of the union A|B
I had
AB = compose A' of a:A end
| compose B' of b:B end;
While the definition of AB
looks a little clumsy, its use in specifications is actually quite nice (function definitions look similar to those over A1|B1
while retaining the benefits of the simple A
and B
). Example
lenab: AB +> nat
lenab(ab) == cases ab:
mk_A'(a) -> len a,
mk_B'(b) -> len b
end;
I recall discussing with Nick some time back and he pointed out the a
and b
tags in the definition can be dropped (I have yet to come across a situation where I wanted to use them so it seems a good general rule).
This was the simplest way I could think of being able to handle the situation where the immediately obvious solution is a union of non-disjoint types. I think this is an example of Peter's "good practice". Of course, if anyone has a better approach...
I'll look at adding a TC warning for comparisons that use union types with (potentially) different orderings. It should be easy, though I need to make sure we don't get warnings for implicitly ordered numeric types.
I don't want to get dragged off into VDM++ OO semantics again! (We never really resolved whether bindings to statically defined functions/operations were static in the typed sense, though that is what most OO languages do). But I don't really understand why "a < b" is not effectively just a static binding to A.compareTo(b), where A is a's actual type. Why is that different to the object case?
PS - sorry Tomo, I see you said raise an error rather than a warning. But in my mind, I am starting to think of these situations as potential runtime errors that would have proof obligations raised to defend against them. It's similar to comparing "nat | char" with "<". That should produce an is_nat obligation, but you don't get a warning about the possible error (or a TC error - which is more life "definite semantics").
Nick, I think giving warning message would be good. But in that case, we need to give a certain semantics on the overlapped ordered types. (Implementation dependent, aka undefined behavior, might be a choice.)
I'd like to avoid further OO-like semantics, too.
Maybe your current implementation is like bool T::operator<(const T& rhs)
, while I'm thinking of something like bool operator<(const T& lhs, const T& rhs)
if I explain in C++.
What I'm saying is the difference between "overriding" and "overloading".
The reason why I prefer "overloading" semantics over "overriding" semantics is to handle cases like https://github.com/overturetool/language/issues/39#issuecomment-298264341 . Even if we give warnings and POG generates PO to ensure unionOne
and unionTwo
have only one ordered type, the "overriding" semantics can't find the right ordering function. The "overloading" semantics can handle it. Maybe it's rare cases in practice. Can the interpreter/POG detect such cases at compile time?
OK, I've added warnings for any ordering comparison with union arguments that define multiple ord clauses. And you also get a warning when you define a type as a union with an ord clause that overrides member ord clauses. ZIP attached below.
As for semantics with overlapping types, the easiest thing to implement is for the ordering to reflect the ordering of the actual type of the argument (with the caveat that it may not be obvious what the actual type is). In the example below, f(1) returns true because the argument 1 is bound to the type A in the union; if it had bound to B, then it would have returned false. And this is just the sort of mess that the warnings are telling you about!
types
A = nat
ord a < b == a < b;
B = real
ord a < b == a > b;
C = A | B -- warnings about ignoring members' ord clauses
ord a < b == a < b; -- warning on this "<"
functions
f: A | B -> bool
f(x) == x < 10; -- warning on this "<"
Warning 5021: Multiple union members define order clauses, (A, B) in 'DEFAULT' (test.vdm) at line 9:20
Warning 5019: Order of union member A will be overridden in 'DEFAULT' (test.vdm) at line 8:5
Warning 5019: Order of union member B will be overridden in 'DEFAULT' (test.vdm) at line 8:5
Warning 5021: Multiple union members define order clauses, (A, B) in 'DEFAULT' (test.vdm) at line 13:15
Type checked 1 module in 0.257 secs. No type errors and 4 warnings
> p f(1)
= true
Executed in 0.016 secs.
> p f(11)
= false
Executed in 0.002 secs.
>
So what do we need for new proof obligations here?
I've created a new PO type called an ordered obligation. If the left or right of a comparison operator is not completely ordered (ie. some part is not numeric and doesn't define an ord clause) then the obligation creates "and not is_(a, T)" clauses for every part of the type that is not comparable.
At the moment, there is no "is_ord" way to declare that something is ordered, which means we can't declare that @T polymorphic types are ordered. At the moment, it is assuming @T types are ordered, so no POs are generated for their bodies, though a runtime error would result if @T was not ordered and it was used in a comparison. How do we solve that one?
This version of the jar has the POs I mentioned above, as well as some other fixes for nested ord/eq calls (when the ord/eq clause call on to the ord/eq clause of another type).
I can't remember whether I mentioned that I will be on holiday for two weeks from this weekend. I'll try to leave the jar in a reasonable state so that you can carry on testing while I'm away :)
Guys, I'm not sure I'm in favor of warnings -- that is what the PO is for. There are many places in a spec where a PO is needed to guarantee runtime safety and we don't issue warnings there. I think this introduces inconsistency in the tool.
Regarding the PO Nick suggests, we need a notion of "type is ordered". This needs a higher level construct (for example, Haskell type classes).
If we don't have that, we need to express it with subtype POs, something like the value must be of one of the types in the union with a defined order.
So, for
types
A = nat
ord a < b == a < b;
B = real -- note no ord
functions
f: A | B -> bool
f(x) == x < 10;
We could have forall x: A|B & is_(x,A)
-- this cannot be discharged, so the spec is not runtime safe.
Feels clunky to me. Have we considered banning these kinds of comparisons? Paul, have you found the need to compare values of union types?
Identification of the Originator
Paul Chisholm
Target of the request, defining the affected components of the language definition
VDM-SL order relation
Motivation for the request
All types in VDM-SL admit equality via a primitive (i.e. built-in) equality relation. Numeric types in VDM-SL are the only types that admit an order relation. A general purpose order relation can be defined for most types in VDM-SL, in a similar manner to equality. We need only define the < relation; equality plus < allows the derivation of <=, > and >=.
Order is ubiquitous. After equality, order is the most commonly used relation in computing. When specifying a data type, we very often require an order relation on the type. In VDM-SL we must define a truth valued function that implements the order relation. If we want the familiar complement of operators (<, <=, >, >=) then we must define four functions. Comparisons in a specification then employ the functions, but using function application notation rather than the familiar symbolic infix notation.
The Overture VDM-SL example ISO8601 specifies types for date, time, date/time and duration. The effective use of these types in specifications requires an order relation on each of the four types. Sixteen functions are specified: <, <=, >, >= on each of the four types. In every case the order relation is the ‘obvious’ or ‘natural’ ordering one would expect.
Description of the request
The attached VDM-SL module ‘Order.vdmsl’ contains a model of VDM-SL values and defines an order relation over those values. A notion of ‘comparable’ values is defined, with the order relation being total with respect to comparability. The primitive VDM-SL order relation on numeric values could be extended to the order relation defined in ‘Order.vdmsl’.
Notes:
a=b
anda<b
to be true.description of the modification
benefits from the modification
possible side effects
None - existing specifications are semantically unchanged
A test suite for validation of the modification in the executable models (if appropriate).
None
The following is the specification of the order relation as a VDM-SL model.
Order.vdmsl.txt