In static type systems, subtyping is used to determine when a value of one type can be safely used at another type. It is often convenient to think of subtyping T S in terms of the set inclusion: “the elements of T are a subset of the elements of S” (Pierce, 2002). This intuition is not always correct, but, in the case of semantic subtyping (Hosoya and Pierce, 2003; Frisch et al., 2008; Ancona and Corradi, 2016), subtyping is defined exactly as the subset relation. Under semantic subtyping, types are interpreted as sets , and subtyping is defined as inclusion of the interpretations .
Subtyping can also be used for run-time dispatch of function calls. For example, object-oriented languages usually support single dispatch — the ability to dispatch a method call based on the run-time type of the receiver object. A more complex form of dispatch is multiple dispatch (MD) (Chambers, 1992; Clifton et al., 2000), which takes into account run-time types of all arguments when dispatching a function call. One way to implement MD is to interpret both function signatures and function calls as tuple types (Leavens and Millstein, 1998) and then use subtyping on these types.
Dynamic dispatch is not limited to statically typed languages, with multiple dispatch being even more widespread among dynamically typed ones, e.g., CLOS, Julia, Clojure. Unlike statically typed languages, which conservatively prevent type errors at compile-time, dynamic languages detect type errors at run-time: whenever an operator is restricted to certain kinds of values, the run-time system checks type tags associated with the operator’s arguments to determine whether it can be safely executed. A type tag indicates the run-time type of a value. Thus, any class that can be instantiated induces a tag — the name of the class — whereas an abstract class or interface does not. Some structural types also give rise to tags, e.g., tuples and sums (tagged unions).
While dynamically typed languages do use subtyping, semantic subtyping is not applicable in this case, for the semantic definition refers to a static typing relation. To enable semantic reasoning in the context of dynamic languages, we propose tag-based semantic subtyping where a type is interpreted as a set of run-time type tags instead of values.
We define tag-based semantic subtyping for a fragment of the Julia language (Bezanson et al., 2017) that includes nominal types, tuples, and unions. Tuples and unions are rather typical for semantic subtyping systems; they have a clear set-theoretic interpretation and make up an expressive subtyping relation where tuples distribute over unions. At the same time, to the best of our knowledge, the interaction of unions with nominal types has not been studied before in the context of semantic subtyping. This interaction introduces an unusual subtyping rule between abstract nominal types and unions, with implications for multiple dispatch. Note that the combination of unions and nominal types is not unique to Julia; for instance, it also appears in the statically typed language Ceylon (King, 2017).
Our contributions are as follows:
A definition of tag-based semantic subtyping for nominal types, tuples, and unions (Sec. 2).
Proof of decidability of reductive subtyping (App. C).
Discussion of the implications of using semantic subtyping for multiple dispatch, as well as an alternative semantic interpretation of nominal types (Sec. 5).
2. Semantic Subtyping in MiniJl
We base our work on a small language of types MiniJl, presented in Fig. 1. Types, denoted by , include pairs, unions, and nominal types; denotes concrete nominal types that can be instantiated, and denotes abstract nominal types.
We work with a particular hierarchy of nominal types (presented in Fig. 1 as a tree) instead of a generic class table to simplify the development. There are four concrete leaf types (depicted in rectangles) and two abstract types in the hierarchy. Formally, the hierarchy can be represented with a list of declarations read as “ extends ” where is either or . In the case of MiniJl, the hierarchy is defined as follows:
Nominal hierarchies should not have cycles, and each type can have only one parent.
Only instantiatable types induce type tags, which we call value types. Their formal definition is given in Fig. 2: value type is either a concrete nominal type or a pair of value types. For example, , , and are all value types. Union types, like abstract nominal types, are not value types. Therefore, a type such as is not a value type despite it describing the same set of values as the value type .
2.1. Semantic Interpretation of Types
As mentioned in Sec. 1, we interpret a type as a set of type tags (i.e. value types) instead of values and call this semantic interpretation tag-based. Formally, the interpretation is given by the function that maps a type into a set of value types , as presented in Fig. 3.
A type’s interpretation states what values constitute the type: means that values tagged with (i.e. instances of ) belong to . Thus, in MiniJl, a concrete nominal type is comprised only of its direct instances.111In the general case, the interpretation of a concrete nominal type would include the type and all its concrete subtypes. Abstract nominal types cannot be instantiated, but their interpretation needs to reflect the nominal hierarchy. For example, a value is either a concrete complex or real number, which in turn is either a concrete integer or a floating point value. Therefore, the set of value types describes the set of all possible values of type . More generally, the interpretation of an abstract nominal type can be given as follows:
where the relation means that nominal type transitively extends : *[right=] ∈NomHrc
. Finally, pairs and unions are interpreted set-theoretically as in standard semantic subtyping.
Once we have the tag interpretation of types, we define tag-based semantic subtyping in the usual manner — as the subset relation:
3. Syntactic Definitions of Subtyping
While the semantic approach does enable intuitive set-theoretic reasoning about subtyping, a subtyping relation also needs to be computable. However, the semantic definition (1) does not suit this purpose, as it operates on interpretations. In the general case, the interpretation of a type can be an infinite set, and as such, it cannot be computed. In the finite case, generating the interpretation sets and checking the subset relation on them would be inefficient. Therefore, we provide an alternative, syntactic definition of subtyping that is equivalent to (1) and straightforward to implement.
We do this in two steps. First, we give an inductive declarative definition that is handy to reason about and prove it equivalent to the semantic definition. Second, we provide a reductive analytic222Inference rules are called analytic (Martin-Löf, 1994) if there is a finite number of rules applicable to a judgment, and the premises of each rule are comprised of the subcomponents of its conclusion. Such rules give rise to a straightforward bottom-up algorithm. If there is always only one rule applicable to a judgment, analytic rules are called syntax-directed. definition of subtyping and prove it equivalent to the declarative one (and, hence, the semantic definition as well). We prove that the reductive subtyping relation is decidable, i.e. for any two types and , it is possible to prove that either is a subtype of or it is not. The proofs are mechanized in Coq, and since Coq logic is constructive, the decidability proof is also a subtyping algorithm. The algorithm can also be implemented as a straightforward recursive function.
3.1. Declarative Subtyping
The declarative syntactic definition of subtyping is provided in Fig. 4. It comprises most of the standard rules of syntactic subtyping for unions and pairs: reflexivity and transitivity (SD-Refl and SD-Trans), subtyping of pairs (SD-Pairs), and subtyping of unions (SD-UnionL, SD-UnionR1, SD-UnionR2). Though SD-UnionR* rules are seemingly very strict (they require the left-hand side type to be syntactically equivalent to a part of the right-hand side type), transitivity allows us to derive judgments such as via and .
Note that all rules from Fig. 4 are essential
for the definition to be equivalent to semantic subtyping.
Thus, for example, the syntactic definition needs to be
reflexive and transitive because so is the subset relation,
which is used to define semantic subtyping.
Semantic subtyping also forces us to add rules
for distributing pairs over unions, SD-Distr1 and SD-Distr2.
For instance, consider two types,
They have the same semantic interpretation —
so they are equivalent.
Therefore, we should also be able to derive their equivalence
using the declarative definition,
i.e. declarative subtyping should hold in both directions.
One direction is trivial:
*[right=] … . But the other direction,
cannot be derived without SD-Distr2 rule.
The novel part of the definition resides in subtyping of nominal types. There are four obvious rules coming directly from the nominal hierarchy, for instance, SD-RealNum mirrors the fact that . But the rules SD-RealUnion and SD-NumUnion (highlighted in Fig. 4) are new, dictated by semantic subtyping. Thus, SD-RealUnion allows us to prove the equivalence of types and , which are both interpreted as .
3.2. Reductive Subtyping
The declarative definition is neither syntax-directed nor analytic and cannot be directly turned into a subtyping algorithm. For one, the transitivity rule SD-Trans overlaps with any other rule in the system and also requires “coming up” with an intermediate type to conclude . For instance, to derive
we need to apply transitivity several times, in particular, with the intermediate type . Another source of overlap is the reflexivity and distributivity rules.
By contrast, the rules of reductive subtyping enable straightforward bottom up reasoning; the rules are presented in Fig. 5. The reductive definition lacks the most problematic rules of declarative subtyping, i.e. general reflexivity, transitivity, and distributivity. Some of the inductive rules have the exact declarative counterparts, e.g. subtyping of pairs (SR-Pair) or subtyping of a union on the left (SR-UnionL).
The differing rules are highlighted. The explicit reflexivity rule SR-BaseRefl now only works with concrete nominal types, but this already makes the reductive definition reflexive. The definition also has to be transitive, so several rules are added or modified to enable derivations that used to rely on transitivity in the declarative definition. These include subtyping of nominal types (SR-IntNum, SR-FltNum), subtyping of a union on the right (SR-UnionR1, SR-UnionR2), and normalization (SR-NF).
The last rule of the definition, SR-NF, is the most important, as it covers all useful interactions of transitivity and distributivity that are possible in the declarative definition. The rule rewrites type into its normal form before applying other subtyping rules. Any normalized type has the form , i.e. a union of value types (we omit parenthesis because union is associative). The normalization function is presented in Fig. 6 (the auxiliary function can be found in Fig. 9, App. A). It produces a type in disjunctive normal form by replacing an abstract nominal type with the union of all its concrete subtypes, and a pair of unions with the union of pairs of value types (each of this pairs is itself a value type), for instance:
As shown in Sec. 4.1, a type and its normal form are equivalent according to the declarative definition. This property is essential for the reductive subtyping being equivalent to the declarative one.
The reductive rules are analytic, and if a derivation of exists, it can always be found by the following algorithm.
Use the normalization rule SR-NF once (normalize );
Use all the other rules to derive in the standard manner, bottom up; except for an overlap between SR-UnionR1 and SR-UnionR2, these rules are syntax-directed.
However, this algorithm does not always produce the shortest derivation. For instance, for , it produces a derivation with eight applications of the rules, whereas the shortest derivation needs only five applications (see App. B). It is possible that in practice, an algorithm that tries the short path first and only then resorts to normalization would work better.
The actual Julia implementation uses a clever algorithm to check subtyping of tuples and unions without having to normalize types (Chung et al., 2019). The algorithm is equivalent to the normalization-based one discussed above, but instead of computing the whole normal form, it computes only the components of the normalized type, one at a time.
Note that the rules for subtyping of nominal types do not have to be built-in. Instead of five separate rules, as presented in Fig. 5, we can use a single rule that relies on the relation ( transitively extends ) from Sec. 2.1: *[right=SR-Nom] . Then, for any and , the relation can be checked algorithmically, using the nominal hierarchy NomHrc.
4. Properties of Subtyping Relations
4.1. Correctness of Declarative Subtyping
In order to show correctness of declarative subtyping, we need to prove that the declarative definition of subtyping is sound and complete with respect to the semantic definition. Formally, we write this statement as:
Instead of directly proving (2), it is more convenient to prove the equivalence of declarative subtyping to the following relation (referred to as matching-based semantic subtyping):
Tag-based and matching-based semantic subtyping relations are equivalent:
Since is equivalent to and the equivalence relation is transitive, it suffices to prove the following theorem to show (2).
Theorem 1 (Correctness of Declarative Subtyping).
The other direction of Theorem 1 is more challenging:
In this case, in the definition (3) of , the only value types that match and are of . By (5), we know that matching implies subtyping, so we conclude that all . From the latter, it is easy to show that because, according to the SD-UnionL rule, subtyping of the left-hand side union amounts to subtyping its components. To show (7), we need several more facts in addition to (8).
Function produces a type in normal form:
Normalized type is equivalent to the source type:
Normalization preserves the subtyping relation:
4.2. Reductive Subtyping
Since we have already shown that declarative subtyping is equivalent to semantic subtyping, it suffices to show that reductive subtyping is equivalent to declarative subtyping:
Theorem 2 (Correctness of Reductive Subtyping).
The proof is split into two parts: soundness and completeness. For soundness (completeness), we show that for each SR- rule (SD- rule) it is possible to build a corresponding declarative (reductive) derivation using SD- rules (SR- rules).
The soundness direction is mostly straightforward, as most SR- rules have an immediate SD- counterpart (or require one extra application of transitivity). In the case of SR-NF, the induction hypothesis of the proof, , and the fact that according to (10), allow to conclude .
The challenging part of the proof is to show completeness, as this requires proving that the reductive definition is reflexive, transitive, and distributive (App. C).
Theorem 3 (Decidability of Reductive Subtyping).
To prove the theorem, it suffices to show that reductive subtyping is decidable when is in normal form. This is done by induction on a derivation of . We refer the reader to App. C for more details.
5. Semantic Subtyping and
Multiple Dynamic Dispatch
We set out to define semantic subtyping that can be useful in the context of dynamic languages, however, the semantic definition we presented appears to have an undesired implication for dynamic dispatch. In this section, using multiple dispatch as a running example, we discuss the implication and suggest a solution.
Consider the following methods333In the context of MD, different implementations of the same function are usually called methods, and the set of all methods a generic function. of the addition function defined in the Julia syntax (we assume that function flt converts its argument to a float):
and the function call 3 + 5. With multiple dynamic dispatch, the call is resolved at run-time, based on the types of all arguments. But how exactly does method resolution work?
One approach to implementing multiple dispatch, adopted by some languages
such as Julia (Bezanson, 2015),
is to use subtyping on tuple types (Leavens and Millstein, 1998).
Namely, method signatures and function calls are interpreted as tuple types,
and then subtyping is used to determine applicable methods
as well as pick one of them.
In the example above, the three methods are interpreted
as the following types (from top to bottom):
mII Int Int
mFF Flt Flt
mUU (IntFlt) (IntFlt)
and the call as having type cII Int Int. To resolve the call, the language run-time ought to perform two steps.
Find the applicable methods (or raise an error if there are none). For this, subtyping is checked between the type of the call cII and the method signatures. Since cII mII and cII mUU but cII mFF, only two methods are applicable — mII for integers and mUU for mixed-type numbers.
Pick the most specific of the applicable methods (or raise an error if there is an ambiguity). For this, subtyping is checked pairwise between all the applicable methods. In this example, naturally, we would like mII to be called for 3 + 5. And indeed, since mII mUU and mUU mII, the integer addition is picked as the most specific.
As another example, consider the call 3.14 + 5, which type is Flt Int. There is only one applicable method mUU that is a supertype of the call type, so it should be picked.
What happens if the programmer defines several implementations with the same argument types? In the case of a static language, an error can be reported. In the case of a dynamic language, however, the second implementation simply replaces the earlier one in the same way as reassignment to a variable replaces its previous value.
For instance, consider a program that contains the three previous implementations of (+) and also:
According to the semantic subtyping relation, type Real is equivalent to IntFlt in MiniJl. Therefore, the implementation of mRR will replace mUU defined earlier, and the mixed-type call 3.14 + 5 will be dispatched to mRR.
But there is a problem: the semantics of the program above will change if the programmer adds a new subtype of Real into the nominal hierarchy, e.g. Int8 Real. In this case, type Real stops being equivalent to IntFlt and becomes equivalent to IntFltInt8. Thus, when the program is re-run, type mUU will be a strict subtype of mRR, so the implementation of mRR will not replace mUU. Therefore, this time, the call 3.14 + 5 will be dispatched to mUU, not mRR as before.
We can gain stability by removing subtyping rules that equate abstract nominal types with the union of their subtypes (i.e. SD-RealUnion and SD-NumUnion in the declarative definition444To get equivalent reductive subtyping, we need to change the SR-NF rule by replacing normalization function with (Fig. 11, App. A). from Fig. 4). Then, to fix the discrepancy between the new definition and semantic subtyping, the latter should be modified. To account for potential extension of the nominal hierarchy, abstract nominal type can be interpreted as containing an extra element — “a future subtype of ”. In the case of MiniJl, the new interpretation is as follows:
It can be shown that the modified declarative definition of subtyping is equivalent to semantic subtyping based upon the new interpretation.555The proof can be found in FullAtomicJl folder of (Belyakova, 2018).
6. Related Work
Semantic subtyping has been studied primarily in the context of statically typed languages with structural typing. For example, Hosoya and Pierce (2003) defined a semantic type system for XML that incorporates unions, products, and recursive types, with a subtyping algorithm based on tree automata (Hosoya et al., 2005). Frisch et al. (2008) presented decidable semantic subtyping for a language with functions, products, and boolean combinators (union, intersection, negation); the decision procedure for is based on checking the emptiness of . Dardha et al. (2013) adopted semantic subtyping to objects with structural types, and Ancona and Corradi (2016) proposed decidable semantic subtyping for mutable records. Unlike these works, we are interested in applying semantic reasoning to a dynamic language with nominal types.
Though multiple dispatch is more often found in dynamic languages, there has been research on safe integration of dynamic dispatch into statically typed languages (Chambers, 1992; Castagna et al., 1992; Clifton et al., 2000; Allen et al., 2011; Park et al., 2019). There, subtyping is used for both static type checking and dynamic method resolution. In the realm of dynamic languages, Bezanson (2015) employed subtyping for multiple dynamic dispatch in the Julia language. Julia has a rich language of type annotations (including, but not limited to, nominal types, tuples, and unions) and a complex subtyping relation (Zappa Nardelli et al., 2018). However, it is not clear whether the subtyping relation is decidable or even transitive, and transitivity of subtyping is important for correct implementation of method resolution. In this paper, while we work with only a subset of Julia types, subtyping is transitive and decidable.
Recently, a framework for building transitive, distributive, and decidable subtyping of union and intersection types was proposed by Muehlboeck and Tate (2018). Our language of types does not have intersection types but features pair types that distribute over unions in a similar fashion.
Finally, Chung et al. (2019) proved that Julia’s algorithm for subtyping tuples, unions, and primitive types (without a nominal hierarchy) is equivalent to a semantic subtyping model similar to ours. Combined with our results, this shows that a normalization-based subtyping algorithm for tuples and unions can be implemented efficiently.
7. Conclusion and Future Work
We have presented a decidable relation for subtyping of nominal types, tuples, and unions. Our system has the advantages of semantic subtyping, such as simple set-theoretic reasoning, yet it can be used in the context of dynamically typed languages. We interpret types in terms of type tags, as is typical for dynamic languages, and provide a decidable syntactic subtyping relation equivalent to the subset relation of the interpretations (aka tag-based semantic subtyping).
We found that the initially proposed subtyping relation, if used for dynamic dispatch, would make the semantics of dynamically typed programs unstable due to an interaction of abstract nominal types and unions. A slightly different semantic interpretation of nominal types appeared to fix the issue, and we would like to further explore this alternative.
In future work, we plan to extend tag-based semantic subtyping to top and bottom types, and also invariant type constructors such as parametric references :
As usual for invariant constructors, we would like types such as and to be equivalent. However, a naive interpretation of invariant types below is not well defined because to find all s.t. , we need to already know all the interpretations:
Our plan is to introduce an indexed interpretation
and define semantic subtyping as:
Acknowledgements.We are grateful to Ryan Culpepper, Artem Pelenitsyn, and Mitchell Wand for insightful conversations. We thank Ellen Arteca, Benjamin Chung, Jane Kokernak, Artem Pelenitsyn, Alexi Turcotte, Jan Vitek, and anonymous reviewers for feedback on earlier drafts of the paper.
- Type checking modular multiple dispatch with parametric polymorphism and multiple inheritance. SIGPLAN Not. 46 (10), pp. 973–992. External Links: Cited by: §6.
- Semantic subtyping for imperative object-oriented languages. In Proceedings of the 2016 ACM SIGPLAN International Conference on Object-Oriented Programming, Systems, Languages, and Applications, OOPSLA 2016, New York, NY, USA, pp. 568–587. External Links: Cited by: §1, §6.
- Coq mechanization of MiniJl. External Links: Cited by: Appendix C, §4.1, footnote 5.
- Julia: a fresh approach to numerical computing. SIAM review 59 (1), pp. 65–98. External Links: Cited by: §1.
- Abstraction in technical computing. Cited by: §5, §6.
- A calculus for overloaded functions with subtyping. In Proceedings of the 1992 ACM Conference on LISP and Functional Programming, LFP ’92, New York, NY, USA, pp. 182–192. External Links: Cited by: §6.
- Object-oriented multi-methods in cecil. In Proceedings of the European Conference on Object-Oriented Programming, ECOOP ’92, Berlin, Heidelberg, pp. 33–56. External Links: Cited by: §1, §6.
- Julia’s efficient algorithm for subtyping unions and covariant tuples (pearl). In 33rd European Conference on Object-Oriented Programming (ECOOP 2019), pp. (To appear). Cited by: §3.2, §6.
- MultiJava: modular open classes and symmetric multiple dispatch for java. In Proceedings of the 15th ACM SIGPLAN Conference on Object-oriented Programming, Systems, Languages, and Applications, OOPSLA ’00, New York, NY, USA, pp. 130–145. External Links: Cited by: §1, §6.
- Semantic subtyping for objects and classes. In Formal Techniques for Distributed Systems, D. Beyer and M. Boreale (Eds.), Berlin, Heidelberg, pp. 66–82. Cited by: §6.
- Semantic subtyping: dealing set-theoretically with function, union, intersection, and negation types. J. ACM 55 (4), pp. 19:1–19:64. External Links: Cited by: §1, §6.
- XDuce: a statically typed xml processing language. ACM Trans. Internet Technol. 3 (2), pp. 117–148. External Links: Cited by: §1, §6.
- Regular expression types for xml. ACM Trans. Program. Lang. Syst. 27 (1), pp. 46–90. External Links: Cited by: §6.
- The ceylon language specification, version 1.3. External Links: Cited by: §1.
- Multiple dispatch as dispatch on tuples. In Proceedings of the 13th ACM SIGPLAN Conference on Object-oriented Programming, Systems, Languages, and Applications, OOPSLA ’98, New York, NY, USA, pp. 374–387. External Links: Cited by: §1, §5.
- Analytic and synthetic judgements in type theory. In Kant and Contemporary Epistemology, P. Parrini (Ed.), pp. 87–99. External Links: Cited by: footnote 2.
- Empowering union and intersection types with integrated subtyping. Proc. ACM Program. Lang. 2 (OOPSLA), pp. 112:1–112:29. External Links: Cited by: §6.
Polymorphic symmetric multiple dispatch with variance. Proc. ACM Program. Lang. 3 (POPL), pp. 11:1–11:28. External Links: Cited by: §6.
- Types and programming languages. 1st edition, The MIT Press. External Links: Cited by: §1.
- Julia subtyping: a rational reconstruction. Proc. ACM Program. Lang. 2 (OOPSLA), pp. 113:1–113:27. External Links: Cited by: §6.
Appendix A Normal Forms
Appendix B Non-unique Derivations
There are two derivations of
The shortest derivation:
[FltReal] The normalization-based derivation: *[right=NF] *[right=UnionL]
Appendix C Overview of Coq Proofs
Most of the relevant definitions are in MiniJl/BaseDefs.v. In the table below, we show the correspondence between paper definitions (left column) and Coq definitions (middle column), possibly with syntactic sugar (right column).
|match_ty v t|||- v <$ t|
|sem_sub t1 t2||||- [t1] <= [t2]|
|sub_d t1 t2|||- t1 << t2|
|sub_r t1 t2|||- t1 << t2|
|unite_pairs t1 t2|
c.2. Basic Properties of Normalization Function
File MiniJl/BaseProps.v contains several simple properties that are needed for proving the major theorems discussed in the paper, in particular, the following properties of the normalization function :
|Statement||Ref in text||Name in Coq|
c.3. Basic Properties of Matching Relation
The following properties are proven in MiniJl/PropsMatch.v.
Matching relation is reflexive, match_valty__rflxv (by induction on ):
The only value type that a value type matches is the value type itself, valty_match_valty__equal (by induction on ):
The matching relation is decidable, match_ty__dcdbl (by induction on , then by induction on ):
c.4. Correctness of Declarative Subtyping
First, we discuss some auxiliary statements that are needed for proving Theorem 1 (located in MiniJl/DeclSubProp.v).
One direction of (5),
is proven in match_ty__sub_d_sound by induction on . The other direction,
is proven in match_valty__sub_d_complete by induction on . The transitivity case, SD-Trans, requires a helper statement, match_valty__transitive_on_sub_d:
which is proven by induction on .
The equivalence of a type and its normal form (10) is shown by induction on in lemmas mk_nf__sub_d1 () and mk_nf__sub_d2 ().
Semantic completeness of declarative subtyping for a normalized type (8),
is shown in nf_sem_sub__sub_d by induction on .
When , we use (12).
By definition of , we know that
follows from .
When , we use induction hypothesis and , SD-UnionL rule, and the fact that
c.5. Correctness of Reductive Subtyping
is proven by induction on . The only interesting case is the rule SR-NF where we have the induction hypothesis and need to show . Since , we can use transitivity (rule SD-Trans).
The completeness part of Theorem 2 (lemma sub_r__complete in MiniJl/Props.v),
is ultimately proven by induction on . However, the proof requires showing that reductive subtyping satisfies the following properties (defined in MiniJl/RedSubProps.v):
Reflexivity, sub_r__reflexive (by induction on ):
Distributivity of pairs over unions:
The transitivity proof is done by induction on . In some cases it relies on the fact that subtyping a type is the same as subtyping its normal form,
The right-to-left part follows from SR-NF, and the left-to-right is shown by induction on (sub_r__mk_nf_sub_r1). In the SR-Pair case of the transitivity proof, we also need to perform induction on . The last case, SR-NF, uses the two auxiliary facts:
proven in sub_r__mk_nf_sub_r by induction on