Giuseppe Castagna

Mickaël Laurent

Kim Nguyen

Kim Nguy

Polymorphic Type Inference for Dynamic Languages Reconstructing Types for Systems Combining Parametric, Ad-Hoc, and Subtyping Polymorphism

Keywords: CCS Concepts:, Theory of computation → Type structures, Program analysis, • Software and its engineering → Functional languages, Polymorphism polymorphism, union types, intersection types, type reconstruction

We present a type system that combines, in a controlled way, first-order polymorphism with intersection types, union types, and subtyping, and prove its safety. We then define a type reconstruction algorithm that is sound and terminating. This yields a system in which unannotated functions are given polymorphic types (thanks to Hindley-Milner) that can express the overloaded behavior of the functions they type (thanks to the intersection introduction rule) and that are deduced by applying advanced techniques of type narrowing (thanks to the union elimination rule). This makes the system a prime candidate to type dynamic languages.

INTRODUCTION

Typing dynamic languages is a challenging endeavour even for very simple pieces of code. For instance, JavaScript's logical or operator "||" behaves like the following function (also in JavaScript): 1 1 function lOr (x, y) { 2 if (x) { return x; } else { return y; } 3 } A naive type for this function is (Bool, Bool) → Bool, which states that lOr is a function that takes two Boolean arguments and returns a Boolean result. This however is an overly restrictive type, that does not account for the fact that in JavaScript logical operators such as lOR can be applied to any pairs of arguments, not just to Boolean ones. JavaScript distinguishes two kinds of values: eight "falsy" values (i.e., false, "", 0, -0, 0n, undefined, null, and NaN) and the "truthy" values (all the others). The expression if executes the else code if and only if the tested value is falsy. If we want to change the previous type to account for this fact, then we should give lOr the type (Any, Any) → Any (where Any is the type of all values), which is a rather useless type since it essentially states that lOr is a binary function. To give lOr a more informative type, we need union and intersection types (which are already integrated in typed versions of JavaScript such as TypeScript [Microsoft] and Flow [Facebook]): we define the type Falsy as the following union type false ∨ "" ∨ 0 ∨ -0 ∨ 0n ∨ undefined ∨ null ∨ NaN, where each value denotes here the singleton type containing that value, and the type Truthy to be its complement, ¬Falsy, that is, the type of all values that are not of type Falsy. Then we can deduce for lOr the following more precise type ((Truthy, Any) → Truthy) ∧ ((Falsy, Truthy) → Truthy) ∧ ((Falsy, Falsy) → Falsy)

(1)

In this type, ∧ is a type combinator denoting intersection and meaning that the function has all the types given in the intersection: that is, in words, if the first argument of a function of this type is a Truthy, then the function returns a Truthy regardless of the second argument (first arrow type), while if the first argument is a Falsy, then the result is of the same type as the second argument's type (second and third arrow type). Notice how the use of an intersection of arrow types corresponds to the typing of an "overloading" behavior (also known as, ad hoc polymorphism [START_REF] Strachey | Fundamental concepts in programming languages[END_REF]), insofar as the result of an application depends on the type of the input.

In order to derive such a type, the type system must deduce that whenever the condition tested by the if holds, then x is of type Truthy and, therefore, (𝑖) that all occurrences of x in the "then" branch (here just one) have type Truthy and (𝑖𝑖) that all the occurrences of the same variable x in the branch "else" (here none) have thus type Falsy. This kind of deduction is usually referred as type narrowing or occurrence typing since it requires to "narrow" the type of a variable x differently for its different occurrences. A type system such as the one for Typed Racket-defined in [START_REF] Tobin | Logical types for untyped languages[END_REF] where the term occurrence typing was first introduced-is able to check that lOr has the type in (1), meaning that the deduction requires the programmer to explicitly specify the type in the code. The system by Castagna et al. [2022b] makes a step further, since not only it can check that lOr has the type in ( 1), but also it can reconstruct for lOr the intersection type ((Truthy, Any) → Truthy) ∧ ((Falsy, Any) → Any) which, although it is less precise a type than (1), it is inferred from the code of lOr as is, without needing any type annotation. This latter work constitutes the state of the art of this kind of inference, since it is the only system that can reconstruct intersections of arrow types.

In this work we go a step further, and show how to infer (i.e., reconstruct) intersections of polymorphic function types. In particular, the system we present here reconstructs for lOr the following first order polymorphic type (where 𝛼 and 𝛽 are type variables):2 ∀𝛼, 𝛽 . ((𝛼∧Truthy, Any) → 𝛼∧Truthy) ∧ ((Falsy, 𝛽) → 𝛽)

(2)

This type completely specifies the semantics of the function lOR: it states that if the first argument is a Truthy, then the application of the function returns the first argument,3 otherwise it returns the second argument. This type is more precise than the one in (1), since it allows the system to deduce that, say, if the first argument of lOR is an object, then the result will be an object of the same type (rather than just a truthy value). Not only does the system we present here infers such a precise type, but this kind of precision is compositional, yielding an accurate type also for the expressions in which the function is used. For instance, if we define the following function: then, as we explain later on, our system infers that id has type ∀𝛼 .𝛼 → 𝛼, viz., that id is indeed the polymorphic identity function. This is clearly better than the current state of the art. Still, it does not seem too hard a feat to deduce that if we are testing whether x is a truthy value, then when the test succeeds we can assume that x is of type Truthy. To show the more advanced capabilities of our system let us have a look at how ECMAScript specifies the semantics of JavaScript logical operators, as defined in the 2021 version of the specification [Ecma 2021, Section 13.13.1]. Since in JavaScript there are no union or intersection types, then the falsy and truthy values are defined via an (abstract) function ToBoolean which simply checks whether its argument is one of the 8 falsy values and returns false, otherwise it returns true (see its definition in row 1 of Table 1 in Section 5). In our system, ToBoolean has type (Truthy → true) ∧ (Falsy → false). All logical operators are then defined by ECMAScript in terms of this function: this has the advantage that any change to the specification of falsy (e.g., the addition of a new falsy value, like the addition of the built-in bigint type and its constant 0n in ES2020) requires only the modification of this function, and is automatically propagated to all operators. So the actual definition of lOr for ECMAScript is the following one: If we feed this function to our system, then it infers for it the type in (2), that is, the same type it already deduced for the simpler version of lOr defined in lines 1-3. But here the deduction needed to perform type narrowing is more challenging, since the system must deduce from the type (Truthy → true) ∧ (Falsy → false) of ToBoolean that when the application in line 8 returns a truthy value, then the argument of ToBoolean is of type Truthy, and it is of type Falsy otherwise. More generally, we need a system which, when a test is performed on an arbitrarily complex application, can narrow the type of all the variables occurring in the application by exploiting the information provided by the overloaded behavior of the functions therein. Achieving such a degree of precision is a hard feat but, we argue, it is necessary if we want to reconstruct types for dynamic languages, that is, if we want to type their programs as they are, without requiring the addition of any type annotations. Indeed, the core operators of these languages (e.g., JavaScript's "||", "&&", "typeof", . . . ) are characterized by an "overloaded" behavior, which is then passed over to the functions that use them. So for instance a simple use of JavaScript logical or "||" such as in (x => x || 42) results in a function whose precise type, as reconstructed by our system, is (Falsy → 42) ∧ (Truthy ∧ 𝛼 → Truthy ∧ 𝛼). JavaScript functions also routinely perform dynamic checks against constants (notably null and undefined), which our system also handles as part of its more general approach to type narrowing of arbitrary expressions.

Outline

Type System (Section 2). So, how can we achieve all this? Conceptually, it is quite simple: we just merge together three of the most expressive type systems studied in the literature, namely the Hindley-Milner (HM) polymorphic types [START_REF] Hindley | The Principal Type-Scheme of an Object in Combinatory Logic[END_REF][START_REF] Milner | A theory of type polymorphism in programming[END_REF], intersection types [START_REF] Coppo | Functional Characters of Solvable Terms[END_REF], and union types [START_REF] Barbanera | Intersection and Union Types[END_REF][START_REF] Macqueen | An ideal model for recursive polymorphic types[END_REF]]. We achieve it simply by putting together in a controlled way the deduction rules characteristic of each of these systems (see Figure 2 in Section 2) and proving that the resulting system is sound (cf., Theorem 2.2).

More precisely, the type system we describe in Section 2 is pretty straightforward. Its core is a classic HM system with first order polymorphism: a program is a list of let-bindings that define polymorphic functions; these are typed by inferring a type for the expressions that define them, this type is then generalized, yielding a prenex polymorphic type for the function. As usual, the deduction of the type of each of these expressions is performed in a type environment that records the generic types for the previously-defined polymorphic functions, and the type system can instantiate these types differently for each use of the polymorphic functions in the expression. The novelty of our system is that when deducing the types of the expressions that define the polymorphic functions, the type system can use not only instantiations of polymorphic types (rule [Inst] in Figure 2), but also intersection and union types. More precisely, to type these expressions the type system can decide to use the classic rules of intersection introduction (rule [∧]) and union elimination (rule [∨]) given in Figure 2. For instance, the intersection introduction rule is used by the system to deduce that since the function lOr (either versions) has both type ((𝛼∧Truthy, Any) → 𝛼∧Truthy) and type ((Falsy, 𝛽) → 𝛽), then it has their intersection, too; this intersection type is then generalized (when lOr is defined at top-level) yielding the polymorphic type in (2). The union elimination rule is essentially used to fine-grainedly type branching expressions and tests involving applications of overloaded functions: for instance, to deduce that the function id in lines 4-6 has type 𝛼 → 𝛼, the system can assume that x has type 𝛼 and separately infer the type of the body for x : (𝛼∧Truthy) and for x : (𝛼∧¬Truthy); since the first deduction yields (𝛼∧Truthy) and the second yields (𝛼∧¬Truthy), then the system deduces that under the hypothesis x : 𝛼, the body has the union of these two types, that is 𝛼. Furthermore, as observed by Castagna et al. [2022b], the combination of the union elimination with the rules of type-cases given in Figure 2 constitutes the essence of narrowing and occurrence typing.

The declarative type system given in Section 2 is all well and good, but how can we define an algorithm that infers whether a given expression can be typed in this system? Rules such as union elimination and intersection introduction are easy to understand, but they do not easily lend themselves to an implementation. In order to arrive to an effective implementation of the type system specified in Section 2 we proceed in two steps: (𝑖) the definition of an algorithmic system and (𝑖𝑖) the definition of a reconstruction algorithm.

Algorithmic System (Section 3). The first step towards an effective implementation of our type system is taken in Section 3 where we define an algorithmic system that is sound and complete with respect to the system of Section 2. The system is algorithmic since it is composed only by syntaxdirected and analytic rules 4 and, as such, is immediately implementable. It is sound and complete since an expression is typable in it if and only if it is typable in the system of Section 2. To obtain this results the system is defined on pairs formed by an MSC-form (Maximal Sharing Canonical form) and an annotation tree. MSC-forms are A-normal forms [START_REF] Sabry | Reasoning about Programs in Continuation-Passing Style[END_REF] on steroids: they are lists of bindings associating variables to expressions in which every proper subexpression is a variable. Their characteristic is that they encode expressions and preserve typability in the sense that every expression is typable if and only if its unique MSC-form is typable. MSC-forms were introduced by Castagna et al. [2022b] to drastically reduce the range of possible applications of the union elimination rule; here we improve their definition to deal with our polymorphic setting and use them for exactly the same reason as in [Castagna et al. 2022b]. Annotation trees encode canonical derivations of the system of Section 2 for the MSC-form they are paired with. They are a generalization of type annotations inserted in the code. Instead of annotating directly an MSC-form with type-annotations we used a separate annotation tree because of the union elimination rule which types several times the same expression under different type environments; this would, thus, require different annotations for the same subexpressions, each annotation depending on the typing context: this naturally yields to tree-shaped annotations in which each branching corresponds either to the different deductions performed by a union elimination rule or to the different deductions performed by an intersection introduction rule. The soundness and completeness properties of the algorithmic systems are thus stated in terms of MSC-forms and annotation trees. They essentially state that an expression 𝑒 has type 𝑡 in the declarative system of Section 2 if and only if there exists a tree annotation for the (unique) MSC-form of 𝑒 that is typable in the algorithmic system with (a subtype of) 𝑡: see Theorem 3.4.

Reconstruction Algorithm (Section 4). The second of the two steps to achieve an effective implementation for the type system of Section 2 is to define a reconstruction algorithm for the previous algorithmic system, which we do in Section 4. The statements of the soundness and completeness properties of the algorithmic system clearly suggest what this algorithm is expected to do: given an expression that defines a polymorphic function, the algorithm must transform it into its unique MSC-form and then try to reconstruct an annotation tree for it so that the pair MSC-form and annotation tree is typable in the algorithmic system of Section 3.

The reconstruction is performed by a system of deduction rules that incrementally refines an annotation tree (initially composed of a single node "infer") while exploring the list of bindings of the MSC-form of the expression to type. It mixes two independent mechanisms: one that infers the domain(s) of 𝜆-terms, and the other that performs type narrowing when a typecase is encountered.

The first mechanism is inspired by the algorithm W by [START_REF] Damas | Principal Type-Schemes for Functional Programs[END_REF]: whenever the application of a destructor (e.g., a function application) is encountered, an algorithm finds a substitution (if any) that makes this application well-typed. In the context of a HM type system, the algorithm at issue needs to solve a unification problem (i.e., whether for two given types 𝑠 and 𝑡 there exists a substitution 𝜎 such as 𝑠𝜎 = 𝑡𝜎) which, if solvable, has a principal solution given by a single substitution [START_REF] Alan | A Machine-Oriented Logic Based on the Resolution Principle[END_REF]]. In our system, which is based on subtyping, the algorithm at issue needs to solve a tallying problem (i.e., whether for two given types 𝑠 and 𝑡 there exists a substitution 𝜎 such as 𝑠𝜎 ≤ 𝑡𝜎) which, if solvable, has a principal solution given by a finite set of substitutions [START_REF] Castagna | Polymorphic functions with set-theoretic types. Part 2: local type inference and type reconstruction[END_REF]. When multiple substitutions are found, they are all considered and explored in different branches by adding an intersection branching node in the current annotation tree.

The second mechanism gets inspiration from Castagna et al. [2022b] and refines decompositions made by the union-elimination rule in order to narrow the types of variables in the branches of a typecase expression. When the system encounters a typecase that tests whether some expression 𝑒 has type 𝑡, then the type 𝑠 of the variable bound to 𝑒 (recall that an MSC-form is a list of bindings) is split into 𝑠 ∧𝑡 and 𝑠 ∧ ¬𝑡, and these splits are in turn propagated recursively in order to generate new splits for the types of the variables associated with the subexpressions composing 𝑒. For instance, when the algorithm encounters the test "if (ToBoolean(x))..." at line 8, it splits the type of (the variable bound to) ToBoolean(x) in two, by intersecting it with true and ¬true, and this split in turn generates a new split Truthy and Falsy for the type of the variable x.

The reconstruction algorithm we present in Section 4 is sound: if it returns an annotation tree for an MSC-form, then the pair is typable in the algorithmic system, whose soundness implies that the expression at the origin of the MSC-form is typable in the system of Section 2. At this point, however, it should be pretty obvious that such a reconstruction algorithm cannot be complete. Our system merges three well known systems: first-order parametric polymorphism, intersection types, union elimination. Now, even if parametric polymorphism is decidable, in our system we can encode (and type, via intersection types) polymorphic fixed-point combinators, yielding a system with polymorphic recursion whose inference has been long known to be undecidable [START_REF] Henglein | Type Inference with Polymorphic Recursion[END_REF][START_REF] Assaf | Type Reconstruction in the Presence of Polymorphic Recursion[END_REF]]. Worse, our system includes union elimination, which is one of the most problematic rules from an algorithmic viewpoint, not only because it is neither syntax directed nor analytic, but also because determining an inversion (a.k.a., generation) lemma for this rule is considered by experts the most important open problem in the research on union and intersection types [Dezani 2020], and an inversion lemma is somehow the first step to define a type-inference algorithm, since it tells us when and how to apply the rule. We discuss in detail the reasons and implications of incompleteness in Section 4.4.

Despite being incomplete, our reconstruction algorithm is powerful enough to handle both complicated typing use-cases and common programming patterns of dynamic languages. For instance, for the 𝑍 fixed-point combinator for strict languages 𝑍 = 𝜆𝑓 .(𝜆𝑥 .𝑓 (𝜆𝑣.𝑥𝑥𝑣)) (𝜆𝑥 .𝑓 (𝜆𝑣.𝑥𝑥𝑣)) our algorithm reconstructs the type ∀𝛼, 𝛽, 𝛾 .((𝛼→𝛽) → ((𝛼→𝛽)∧𝛾)) → ((𝛼→𝛽)∧𝛾)) (i.e., in bounded polymorphic notation ∀(𝛼) (𝛽) (𝛾 ≤ 𝛼→𝛽).((𝛼→𝛽) → 𝛾) → 𝛾, cf. Footnote 2). The combinator can then be used as is, to define and infer the type of classic polymorphic functions such as map, fold, concat, reverse, etc., often yielding types more precise than in HM: for instance if we use [𝛼 * ] to denote the type of the lists whose elements have type 𝛼, then the type inferred for (a curried version of) fold_r is ∀𝛼, 𝛽, 𝛾 .((𝛼→𝛽→𝛽)

→ 𝛽 → [𝛼 * ] → 𝛽) ∧ (Any → 𝛾 → [ ] → 𝛾)
where the second type in the intersection states that if the third argument is an empty list, then the result will be the second argument, whatever the type of the first argument is. Finally, we designed our algorithm so that it can take into account explicit type annotations to help it in the inference process. As an example, our algorithm can check that the classic filter function has type ∀𝛼, 𝛽, 𝛾 .((𝛼∧𝛽→Bool) ∧ (𝛼∧¬𝛽→False)) → [𝛼 * ] → [(𝛼∧𝛽) * ], stating that if we pass to filter a predicate that returns false for the elements of 𝛼 that are not in 𝛽, then filtering a list of 𝛼's will return only elements also in 𝛽.

Sections 2, 3, and 4 outlined above constitute the core of our contribution. Section 5 presents our implementation. In Section 6 we discuss related work and Section 7 concludes our presentation. For space reasons we omitted in the main text some rules of the algorithmic and reconstruction systems, as well as all proofs: they are all given in the appendix, available on line as supplemental material.

Discussion, Contributions, and Limitations

Intersections vs. Hindley-Milner. It is a truth universally acknowledged that intersection type systems are more powerful than HM systems: for that, one does not even need full intersections, since Rank 2 intersections suffice. Rank 2 intersection types are types that may contain intersections only to the left of a single arrow and the system of Rank 2 intersection types is able to type all ML programs (i.e., all program typable by HM), has principal typings, decidable type inference, and the complexity of type inference is of the same order as in ML [START_REF] Leivant | Polymorphic Type Inference[END_REF]].

However, intersection type systems are not compositional, and this hinders their use in a modular setting. A program that uses the polymorphic identity function to apply it to, say, an integer and a Boolean, type checks since we can infer that the polymorphic identity function has type (Int→Int)∧(Bool→Bool). But if we want to export this polymorphic identity function to use it in other unforeseen contexts, then we need for it a type that covers all its admissible usages, without the need of retype-checking the function every time it is applied to an argument of a new type. In other words, in a modular usage, parametric polymorphism has an edge over intersection/ad-hoc polymorphism despite being less powerful, since a type such as ∀𝛼 .𝛼→𝛼 synthesizes the infinitely many combinations of intersection types that can be deduced for the identity function; however in a local setting, everything that does not need to be exported can be finer-grainedly typed by intersection types. This division of roles and responsibilities is at the core of our approach. As we show in the next section, programs are lists of bindings from variables to expressions. These expressions are typed in a type environment (generated by the preceding bindings) which binds variables to polymorphic types. These expressions are typed by using instantiation, intersection introduction, and union elimination, but not generalization. Generalization is performed only at top level, that is at the level of programs and reserved to expressions to be used in other contexts.

Parametricity vs. type cases. A parametric polymorphic function (a.k.a., a generic function) is a function that behaves uniformly for all possible types of its arguments, that is, whose behavior does not depend on the type of its arguments. A common way to characterize a generic function is that it is a function that cannot inspect the parametric parts of its input, that is, those parts that are typed by a type variable: these parts can only be either returned as they are, or discarded, or passed to another generic function. Our approach suggests refining this characterization by shifting the attention from inputs as a whole to some particular values among all the possible inputs. This can be seen by comparing the following two function definitions: 𝜆𝑥 .(𝑥 ∈Int) ? 𝑥 : 𝑥 𝜆𝑥 .(𝑥 ∈Int) ? 𝑥 + 1 : 𝑥 Both functions test whether their input is an integer. The function on the right-hand side returns the successor of the argument when this is true and the argument itself otherwise; the one on the left-hand side returns its argument in both cases, that is, it is the identity function.

Our system deduces for the function on the left the type 𝛼 → 𝛼. 5 For the function on the right it returns the type (Int → Int) ∧ (𝛼∖Int → 𝛼∖Int), where 𝑠∖𝑡 denotes the set-theoretic difference of the two types, that is, 𝑠∧¬𝑡. These two types suggest how we can refine the intuitive characterization of parametricity. A generic function can inspect the parametric part of its input (as the function on the left-hand side shows) and its output can depend on this inspection (as the function on the right-hand side shows), but the parts of its output that are typed by a type variable-i.e., the "parametric" parts-cannot depend on it. We can speak of "partial" parametricity, and say that a function is parametric "only" for the inputs (or parts thereof) that are either returned unchanged or discarded: the type variables in its type describe such inputs. For instance, the domain of both the functions above is Any: they both can be applied to any argument. But the first function is parametric for all possible inputs, since the result of the inspection is not used to produce any particular output (it has type ∀𝛼 .𝛼 → 𝛼), while the second function is parametric only for the values of its domain that are not in Int, since it uses the result of the inspection to generate the result for the integer inputs (by subsumption, the second function has type ∀𝛼 . 𝛼 → Int∨(𝛼∖Int): parametricity holds only for the arguments not in Int).

Contributions. The general contribution of our work is twofold. First, it proposes a way to mix parametric and intersection/ad-hoc polymorphism which, in hindsight, is natural: parametric polymorphism for everything defined at top-level and that can thus be used in other contexts (modularity); intersection polymorphism for everything that remains local (for which we can thus use more precise non-modular typing). Second, it proposes an effective way to implement this type discipline by defining a reconstruction algorithm; with respect to that, a fundamental role is played by the analysis of the (type-)tests performed by the expressions, since they drive the way in which types are split: externally, to split the domain of functions yielding intersection of arrows (intersection introduction); internally, to split the type of tested expressions, yielding a precise typing of branching (union elimination). In doing so, it provides the first system that reconstructs types that oncombine parametric and ad hoc polymorphism.

The technical contributions of the work can be summarized as follows:

(1) We define a type system that combines parametric polymorphism with union and intersection types for a functional calculus with type-cases and prove its soundness. (2) We define an algorithmic system that we prove sound and complete with respect to the previous system. (3) We define an algorithm to reconstruct the type annotations of the previous algorithmic system and prove it sound and terminating. The reconstruction algorithm is fully implemented. A prototype which also implements optional type annotations and pattern matching (presented in Appendix A) is available on-line at https: //www.cduce.org/dynlang, and whose sources are on Zenodo: [Castagna et al. 2023b].

Limitations. The system we present here has some limitations. Foremost, the reconstruction algorithm of Section 4 uses backtracking, and at each of its passes it may try to type the same piece of code several times. Backtracking is inherent to our algorithm, since it proceeds by successively refining in different passes, the annotation tree of an MSC-form. The checking of a same piece of code several times at each pass is inherent to the use of unions and intersections: the unionelimination rule repeatedly type-checks the same expression, using different type hypotheses for a given sub-expression; the intersection-introduction rule verifies that an expression has all the types of an intersection, by checking each of them separately. Both features are very penalizing in terms of performance, and any naive implementation of the reconstruction described in Section 4 would yield type-inference times that grow exponentially with the size of the program. Clearly, this is an issue that must be addressed if we want to apply our system to real-world dynamic languages, and further work is needed to frame and/or constrain the current system so that its performance becomes acceptable. Fortunately, the room for improvement is significant: our prototype is an unoptimized proof of concept whose implementation was defined to faithfully simulate the reconstruction inference rules, rather than to obtain an efficient execution; but the simple addition of textbook memoization techniques improved its performance by an order of magnitude (cf. Section 5).

A second limitation of our system is that it is not sound in the presence of side-effects. The algorithm transforms an initial expression into its Maximal Sharing Canonical form, which is a list of bindings, one for each sub-expression of the initial expression. As we explain in Section 3.1.2, these forms are called "maximal sharing" since all equivalent sub-expressions (in the sense stated by Definition 3.1) of the initial expression must be bound by the same variable, so that any refinement of the type of one sub-expression (e.g., as a consequence of a type-case) is passed-through to all equivalent sub-expressions. However, this is sound only if all evaluations of equivalent subexpressions return results that have the same types. While this is true for pure expressions, this can be invalidated by the presence of side-effects. In Section 7 we suggest some research directions on how to modify the equivalence relation of Definition 3.1 to make our system work in the presence of side-effects. Nevertheless, the work presented here is closer to be adapted/adaptable to pure functional languages such as Erlang and Elixir, than to languages such as JavaScript or Python.

Finally, it may be worth pointing out that our approach works only for strict languages, since it uses a semantic subtyping relation that is unsound for call-by-name evaluation strategies [START_REF] Petrucciani | Semantic Subtyping for Non-Strict Languages[END_REF]].

SOURCE LANGUAGE AND TYPE SYSTEM

Syntax and Semantics

Our core language is fully defined in Figure 1. Expressions are an untyped 𝜆-calculus with constants 𝑐, pairs (𝑒, 𝑒), pair projections 𝜋 𝑖 𝑒, and type-cases. A type-case (𝑒 0 ∈𝜏) ? 𝑒 1 : 𝑒 2 is a dynamic type test that first evaluates 𝑒 0 and, then, if 𝑒 0 reduces to a value 𝑣, evaluates 𝑒 1 if 𝑣 has type 𝜏 or 𝑒 2 otherwise. Type-cases cannot test arbitrary types but just ground types (i.e., types without type variables occurring in them) of the form 𝜏 where the only arrow type that can occur in them is 0 → 1, the type of all functions. This means that type-cases can distinguish functions from other values but they cannot distinguish, say, functions that have type Int→Int from those that do not.

Programs are sequences of top-level definitions, ending with an expression that can be seen as the main entry. This notion of program is useful to capture the modularity of our type system. Indeed, top-level definitions are typed sequentially: the type we obtain for a top-level definition is considered definitive and will not be challenged by a later definition.

The reduction semantics for expressions is the one of call-by-value pure 𝜆-calculus with products and with a type-case expression, together with the context rules that implement a leftmost outermost

/𝑥 } 𝜋 1 (𝑣 1 , 𝑣 2 ) 𝑣 1 𝜋 2 (𝑣 1 , 𝑣 2 ) 𝑣 2 (𝑣 ∈𝜏) ? 𝑒 1 : 𝑒 2 𝑒 1 if 𝑣 ∈ 𝜏 (𝑣 ∈𝜏) ? 𝑒 1 : 𝑒 2 𝑒 2 if 𝑣 ∈ ¬𝜏 let 𝑥 = 𝑣 ; 𝑝 Pr 𝑝{𝑣/𝑥 } Dynamic type test 𝑣 ∈ 𝜏 ⇔ typeof (𝑣) ≤ 𝜏, where        typeof (𝑐) = b 𝑐 typeof ((𝑣 1 , 𝑣 2 )) = typeof(𝑣 1 ) × typeof(𝑣 2 ) typeof(𝜆𝑥 .𝑒) = 0 → 1 Evaluation Contexts 𝐸 ::= [ ] | 𝐸𝑒 | 𝑣𝐸 | (𝐸, 𝑒) | (𝑣, 𝐸) | 𝜋 𝑖 𝐸 | (𝐸∈𝜏) ? 𝑒 : 𝑒 𝑃 ::= [ ] | let 𝑥 = [] ; 𝑝 𝑒 𝑒 ′ 𝐸 [𝑒] 𝐸 [𝑒 ′ ] 𝑒 𝑒 ′ 𝑃 [𝑒] Pr 𝑃 [𝑒 ′ ]
Fig. 1. Syntax and semantics of the source language reduction strategy. We use the standard substitution operation 𝑒{𝑒 ′ /𝑥 } that denotes the capture avoiding substitution of 𝑒 ′ for 𝑥 in 𝑒, whose definition we recall in Appendix B. The relation 𝑣 ∈ 𝜏 determines whether a value is of a given type or not and holds true if and only if typeof(𝑣) ≤ 𝜏, where ≤ is the subtyping relation defined by [START_REF] Castagna | Set-theoretic Foundation of Parametric Polymorphism and Subtyping[END_REF] (we recall its definition in Appendix C). Note that typeof(𝑣) maps every 𝜆-abstraction to 0 → 1 and, thus, dynamic type tests do not depend on static type inference. This approximation is allowed by the restriction on arrow types in typecases. Finally, the reduction semantics for programs sequentially reduces top-level definitions, together with a context rule that allows reducing the expression of the first definition.

Types

Types are those by [START_REF] Castagna | Set-theoretic Foundation of Parametric Polymorphism and Subtyping[END_REF] who add type variables to the semantic subtyping framework of [START_REF] Frisch | Semantic Subtyping[END_REF][START_REF] Frisch | Semantic subtyping: dealing set-theoretically with function, union, intersection, and negation types[END_REF].

Definition 2.1 (Types). The set of types Types is formed by the terms 𝑡 coinductively produced by the grammar:

Types 𝑡, 𝑠 ::

= 𝑏 | 𝛼 | 𝜶 | 𝑡 → 𝑡 | 𝑡 × 𝑡 | 𝑡 ∨ 𝑡 | ¬𝑡 | 0
and that satisfy the following conditions: (𝑖) every term has a finite number of different sub-terms (regularity) and (𝑖𝑖) every infinite branch of a term contains an infinite number of occurrences of the arrow or product type constructors (contractivity).

We use the abbreviations For what concerns type variables, we choose not to use type-schemes but rather distinguish two kinds of type variables. Polymorphic type variables ranged over by 𝛼, are type variables that have been generalized and can therefore be instantiated. In a more traditional presentation, such variables are bound by the ∀ of a type-scheme ; the set of polymorphic variables is V 𝑃 . Monomorphic type variables, ranged over by 𝜶 (with bold font), are variables that are not generalized and therefore cannot be instantiated; the set of monomorphic variables is V 𝑀 . Types that only contain monomorphic variables are dubbed monomorphic types 6 :

𝑡 1 ∧ 𝑡 2 = def ¬(¬𝑡 1 ∨ ¬𝑡 2 ), 𝑡 1 ∖ 𝑡 2 = def 𝑡 1 ∧ (¬𝑡 2 ),
Monomorphic types u, v ::= 𝑏 | 𝜶 | u → u | u × u | u ∨ u | ¬u | 0
Our choice of using two disjoint sets for polymorphic and monomorphic type variables, instead of the classical approach of using type schemes ∀𝛼 1 ...𝛼 𝑛 .𝑡, is justified by two reasons. First, type schemes are expected to be equivalent modulo 𝛼-renaming. In our case however, we do not want polymorphic type variables to be freely renamed because of the use, in the algorithmic type system of Section 3, of external annotations containing explicit substitutions over some polymorphic type variables of the context. Secondly, introducing type schemes would require redefining many of the usual set-theoretic type-related definitions, such as the subtyping relation ≤, and the type operators for application • and projections 𝜋 𝑖 . Instead, we obtain a more streamlined theory by making subtyping and these operators ignore whether a variable is polymorphic or monomorphic in the current context, and by explicitly performing instantiations in the type system when required.

The subtyping relation for these types, noted ≤, is the one defined by [START_REF] Castagna | Set-theoretic Foundation of Parametric Polymorphism and Subtyping[END_REF], to which the reader may refer for the formal definition (cf. Appendix C). For this presentation, it suffices to consider that ground types (i.e., types with no variables) are interpreted as sets of values that have that type, and that subtyping is set containment (i.e., a type 𝑠 is a subtype of a type 𝑡 if and only if 𝑡 contains all the values of type 𝑠). In particular, 𝑠 → 𝑡 contains all 𝜆-abstractions that when applied to a value of type 𝑠, if their computation terminates, then they return a result of type 𝑡 (e.g., 0 → 1 is the set of all functions and 1 → 0 is the set of functions that diverge on every argument). Type connectives (i.e., union, intersection, negation) are interpreted as the corresponding settheoretic operators. For what concerns non-ground types (i.e., types with variables occurring in them) all the reader needs to know for this work is that the subtyping relation of [START_REF] Castagna | Set-theoretic Foundation of Parametric Polymorphism and Subtyping[END_REF] is preserved by type-substitutions. Namely, if 𝑠 ≤ 𝑡, then 𝑠𝜎 ≤ 𝑡𝜎 for every type-substitution 𝜎. We use ≃ to denote the symmetric closure of ≤, thus 𝑠 ≃ 𝑡 (read, 𝑠 is equivalent to 𝑡) means that 𝑠 and 𝑡 denote the same set of values and, as such, they are semantically the same type.

Type System

Our type system is given in full in Figure 2. The typing rules for expressions are, to some extent, the usual ones. Constants and variables are typed by the corresponding axioms [Const] and [Ax]. The arrow and product constructors have introduction and elimination rules. Notably, in the case of rule [→I] the type of the argument is monomorphic. The rules for intersection ([∧]) and subtyping ([≤]) are the classical ones, and so is the rule for instantiation ([Inst]) where 𝜎 denotes a substitution from polymorphic variables to types. The type-case construction is handled by three rules: [0]; [∈ 1 ]; [∈ 2 ] . Rule [0] handles the case where the tested expression is known to have the empty type. The other two are symmetric and handle the case when the tested expression is known to have either the type 𝜏 or its negation, in which case the corresponding branch is typed. These rules work together with Rule [∨], which we describe in detail next.

At first sight, the formulation of rule [∨] seems odd, since the ∨ connector does not appear in it. To understand it, consider the classic union elimination rule by [START_REF] Macqueen | An ideal model for recursive polymorphic types[END_REF]:

[∨E] Γ ⊢ 𝑒 ′ : 𝑠 1 ∨𝑠 2 Γ, 𝑥 : 𝑠 1 ⊢ 𝑒 : 𝑡 Γ, 𝑥 : 𝑠 2 ⊢ 𝑒 : 𝑡 Γ ⊢ 𝑒{𝑒 ′ /𝑥 } : 𝑡 Γ ⊢ 𝑒 : 𝑡 Γ ⊢ 𝑒 : 𝑡𝜎 [≤] Γ ⊢ 𝑒 : 𝑡 Γ ⊢ 𝑒 : 𝑡 ′ 𝑡 ≤ 𝑡 ′ Fig. 2. Declarative Type System
Rule [∨E] types an expression that contains occurrences of an expression 𝑒 ′ that has a union type 𝑠 1 ∨ 𝑠 2 ; the rule substitutes in this expression some occurrences of 𝑒 ′ by the variable 𝑥 yielding an expression 𝑒, and then types 𝑒 first under the hypothesis that 𝑥 has type 𝑠 1 and then under the hypothesis that 𝑥 has type 𝑠 2 . If both succeed, then the common type is returned for the expression at issue. As shown by Castagna et al. [2022b], this rule, together with the rules for type-cases, allows the system to perform occurrence typing. For instance, consider the expression (𝑓 𝑦∈Int) ? (𝑓 𝑦) + 1 : false, in the context where 𝑓 has type Any→Any and 𝑦 is of type Any. This expression can be typed thanks to the rule [∨E], by considering the sub-expression 𝑓 𝑦. This subexpression has type Any, which can be seen as the union type Any ≃ Int∨¬Int. We can then replace 𝑥 for 𝑓 𝑦 and type, using [∈ 1 ], the expression (𝑥 ∈Int) ? 𝑥 + 1 : false, with 𝑥 : Int. This yields a type Int (rule [∈ 1 ] ignores the second branch) and by subtyping, the expression has type Int∨False. Likewise for the choice 𝑥 : ¬Int, using rule [∈ 2 ] the second branch has type False and therefore Int∨False (again via subtyping). The whole expression has thus the desired type Int∨False.

A key element is that rule [∨E] guessed how to split the type Any of 𝑓 𝑦 into Int ∨ ¬Int. In a non-polymorphic setting, this is perfectly fine. But in a type-system featuring polymorphism, particular care must be taken when introducing (fresh) type variables. As it is stated, MacQueen et al. 's [∨E] rule could choose to split, say, 1 into a union 𝛼 ∨ ¬𝛼, with 𝛼 a polymorphic type variable. If so, then the rule becomes unsound. As a matter of fact, the premises of the [∨E] behave as in rule [→I], in that they introduce in the typing environment a fresh type whose variables must not be instantiated. In our example, however, in one premise, the rule introduces 𝑥 : 𝛼 in the typing environment which can, for instance, be instantiated by the [Inst] rule. In the second premise, it introduces 𝑥 : ¬𝛼 which can also be instantiated in a completely different way. In other words, the correlation between the two occurrences of the same variable 𝛼 is lost, which amounts to commuting the (implicit) universal quantification with the ∨ type connective, yielding a non-prenex polymorphic type (∀𝛼 .𝛼) ∨ (∀𝛼 .¬𝛼). To avoid this unsound situation, we need to ensure that when a type is split between two components of a union, no polymorphic variable is introduced. This is achieved by rule [∨] which requires the type 𝑠 of 𝑒 ′ to be split as 𝑠 ≡ (𝑠 ∧ u) ∨ (𝑠 ∧ ¬u) (here is our hidden union).

The top-level definitions of a program are typed sequentially by two specific rules:

[TopLevel-Expr] Γ ⊢ 𝑒 : 𝑡 Γ ⊢ Pr 𝑒 : 𝑡𝜙 𝜙#Γ [TopLevel-Let] Γ ⊢ Pr 𝑒 : 𝑡 Γ, 𝑥 : 𝑡 ⊢ Pr 𝑝 : 𝑡 ′ Γ ⊢ Pr let 𝑥 = 𝑒 ; 𝑝 : 𝑡 ′
where 𝜙 denotes a generalization, that is a substitution transforming monomorphic variables into polymorphic ones and where 𝜙#Γ ⇐⇒ def dom(𝜙) ∩ vars(Γ) = ∅. After typing an expression used for a definition, its type is generalized (Rule [Toplevel-Expr]) before being added in the environment (Rule [Toplevel-Let]). Note that this is the only place where generalization takes place: no rule in the type system for expressions (Figure 2) allows the generalization of a type variable. As explained at the beginning of Section 1.2, this is not a limitation, since intersection types are more powerful than HM polymorphism, and top-level generalization is of practical importance since it is necessary to the modularity of type-checking. Nevertheless, the core of our inference system is given only by the rules in Figure 2 for expressions: the above "TopLevel" rules are only useful to inhabit variables of the typing environments used in the rules for expressions, and this makes it possible to close the expressions being typed. For instance, if a typing derivation for an expression 𝑒 is deduced, say, under the hypothesis 𝑥 : 𝛼→𝛼 (with 𝛼 polymorphic), then it is possible to obtain a closed program by inhabiting 𝑥 by a definition like let 𝑥 = 𝜆𝑦.𝑦 ;... ; 𝑒 . This is the reason why, henceforth, we mainly focus on the typing of expressions.

The type system is sound (all proofs for this work are given in Appendix I):

Theorem 2.2 (Soundess). If ∅ ⊢ Pr 𝑝 : 𝜏, then either 𝑝 diverges or 𝑝 Pr 𝑣 with 𝑣 ∈ 𝜏.

ALGORITHMIC SYSTEM

As discussed in the introduction, the declarative type system is not syntax directed and some rules are not analytic. In order to make it algorithmic, we first introduce in Section 3.1 a canonical form for expressions that adds syntactic constructions (bindings) to indicate when to apply the union elimination rule and on which sub-expression. Then, in Section 3.2, we define a fully algorithmic type system that takes a canonical form together with an annotation and produces a type.

MSC Forms

3.1.1 Canonical Forms. The [∨] rule is not syntax directed since it can be applied on any expression and can split the type of any of its subexpressions. If we want an algorithmic type system, we need a syntactic way to determine when to apply this rule, and which subexpression to split. In order to achieve this, we represent our terms with a syntax called Maximal Sharing Canonical Form (MSC Form) introduced by Castagna et al. [2022b]. Let us start by defining the canonical forms, which are expressions produced by the following grammar:

Atomic expressions 𝑎 ::= 𝑐 | 𝑥 | 𝜆𝑥 .𝜅 | (x, x) | xx | 𝜋 𝑖 x | (x∈𝜏) ? x : x Canonical Forms 𝜅 ::= x | bind x = 𝑎 in 𝜅
Canonical forms, ranged over by 𝜅, are binding variables (noted x, y, or z) possibly preceded by a list of definitions (from binding variables to atoms). Atoms are either a variable from a 𝜆-abstraction (noted 𝑥, 𝑦, or 𝑧), or a constant, or a 𝜆-abstraction whose body is a canonical form, or any other expression in which all proper sub-expressions are binding variables. An expression in canonical form without any free binding variable can be transformed into an expression of the source language using the unwinding operator ⌈.⌉ that basically inlines bindings: ⌈bind x = 𝑎 in 𝜅 ⌉ = ⌈𝜅⌉{⌈𝑎⌉/x} (see Appendix E.1 for the full definition). The inverse direction, that is, producing from a source language expression a canonical form that unwinds to it, is straightforward (see Appendix E.2). However for each expression of the source language there are several canonical forms that unwind to it. For our algorithmic type system we need to associate each source language expression to a unique canonical form, as we define next.

3.1.2 Maximal Sharing Canonical Forms. We define a congruence on canonical forms and atoms: Definition 3.1 (Canonical eqivalence). We denote by ≡ 𝜅 the smallest congruence on canonical forms and atoms that is closed by 𝛼-conversion and such that bind

x 1 = 𝑎 1 in bind x 2 = 𝑎 2 in 𝜅 ≡ 𝜅 bind x 2 = 𝑎 2 in bind x 1 = 𝑎 1 in 𝜅 x 1 ∉fv(𝑎 2 ), x 2 ∉fv(𝑎 1 )
To infer types for the source language, we single out canonical forms satisfying three properties: Definition 3.2 (MSC Forms). A maximal sharing canonical form (abbreviated as MSC-form) is (any canonical form 𝛼-equivalent to) a canonical form 𝜅 such that:

(1) if bind x 1 = 𝑎 1 in 𝜅 1 and bind x 2 = 𝑎 2 in 𝜅 2 are distinct sub-expressions of 𝜅, then 𝑎 1 ̸ ≡ 𝜅 𝑎 2 (2) if 𝜆𝑥 .𝜅 1 is a sub-expression of 𝜅 and bind y = 𝑎 in 𝜅 2 a sub-expression of 𝜅 1 , then fv(𝑎) ⊈ fv(𝜆𝑥 .𝜅 1 ) (3) if bind x = 𝑎 in 𝜅 ′ is a sub-expression of 𝜅, then x ∈ fv(𝜅 ′ ).
MSC-forms are defined modulo 𝛼-conversion. 7 The first condition states that distinct variables denote different definitions, that is, it enforces the maximal sharing of common sub-expressions. The second condition requires bindings to extrude 𝜆-abstractions whenever possible. The third condition states that there is no useless bind (bound variables must occur in the body of the binds).

The key property of MSC-forms is that given an expression 𝑒 of the source language, all its MSC-forms (i.e., all MSC-form whose unwinding is 𝑒) are equivalent: Proposition 3.3. If 𝜅 1 and 𝜅 2 are two MSC-forms and

⌈𝜅 1 ⌉ ≡ 𝛼 ⌈𝜅 2 ⌉, then 𝜅 1 ≡ 𝜅 𝜅 2 .
We denote the unique MSC-form whose unwinding is 𝑒 by MSC(𝑒). It is easy to transform a canonical form into a MSC-form that has the same unwinding. The reader can refer to Appendix E for a set of rewriting rules implementing this operation.

Algorithmic Typing Rules

MSC-forms tell us when to apply the [∨] rule: a term bind x = 𝑎 in 𝜅 means (roughly) that it must be typed by applying the union rule to the expression 𝜅{𝑎/x}. Putting an expression into its MSC-form to type it, thus corresponds to applying the [∨] rule on every occurrence of every subexpression of the original expression. This is a step toward a syntax-directed type system. However, there are still two issues to solve before obtaining an algorithmic type system: (𝑖) rules [∧], [Inst], and [≤] are still not syntax-directed, and (𝑖𝑖) rules [∨], [Inst], [→I], and [≤] are not analytic, meaning that some of their premises cannot be deduced just by looking at the conclusion: the [∨] rule requires guessing a type decomposition (i.e., the monomorphic type u in the premises), the [Inst] rule requires guessing a substitution, the [→I] rule requires guessing the domain u of the function, and the [≤] rule requires guessing the type 𝑡 ′ to subsume to.

The issue of [Inst] and [≤] not being syntax directed can be solved by embedding them in some structural rules (in particular, in the rules for destructors). Moreover, as we will see later, the rules in which we embed [≤] can be made analytic by using some type operators. As for rule [∧], making it syntax-directed, is trickier. Indeed, the usual approach of merging rules [→I] and [∧] does not work here, since terms in MSC-forms may hoist a bind definition outside the function where they are used, causing rule [∧] to be needed on a term that is not, syntactically, a 𝜆-abstraction. Lastly, there is no easy way to guess the substitutions used by [Inst] rules, or the domain used in [→I] rules, or the decompositions performed by [∨] rules. To tackle these issues, our algorithmic type system will not only take a canonical form as input, but also an annotation that will (𝑖) indicate when to apply an intersection, and (𝑖𝑖) indicate which type decomposition (for [∨] rules) and which type substitutions (for [Inst] rules) to use. Formally, our algorithmic system uses judgements of the form Γ ⊢ A [𝜅 | k] : 𝑡 for a canonical form 𝜅, and Γ ⊢ A [𝑎 | a] : 𝑡 for an atom 𝑎 where k and a are respectively form annotations and atom annotations defined as follows:

Atom annots a ::= ∅ | 𝜆(u, k) | (𝜌, 𝜌) | @(Σ, Σ) | 𝜋 (Σ) | 0(Σ) | ∈ 1 (Σ) | ∈ 2 (Σ) | ({a, ... , a}) Form annots k ::= 𝜌 | keep (a, {(u, k), . . . , (u, k)}) | skip k | ({k, . . . , k})
where 𝜌 ranges over renamings of polymorphic variables, that is, injective substitutions from V 𝑃 to V 𝑃 , and Σ ranges over instantiations, that is, sets of substitutions from V 𝑃 to Types. We chose to keep annotations separate from the terms, instead of embedding them in the canonical forms, since in the latter case it would be more complicated to capture the tree structure of the derivations.

The algorithmic system is defined by the rules given in Appendix G. Below we comment the most interesting rules (we just omit the rules for constants, variables and two rules for type-cases). Essentially, there is one typing rule for each annotation, the only exception being the ∅ annotation that is used both in the rule to type constants and in the two rules for variables.

[→I-Alg] Γ, 𝑥 : u ⊢ A [𝜅 | k] : 𝑡 Γ ⊢ A [𝜆𝑥 .𝜅 | 𝜆(u, k)] : u → 𝑡
To type the atom 𝜆𝑥 .𝜅, the annotation 𝜆(u, k) provides the domain u of the function, and the annotation k for its body.

[→E-Alg]

Γ ⊢ A [x 1 x 2 | @(Σ 1 , Σ 2 )] : 𝑡 1 • 𝑡 2 𝑡 1 = Γ(x 1 )Σ 1 , 𝑡 2 = Γ(x 2 )Σ 2 𝑡 1 ≤ 0 → 1, 𝑡 2 ≤ dom(𝑡 1 )
To type an application one must apply an instantiation and a subsumption to both the type of the function and the type of the argument. Instantiations (i.e., Σ 1 and Σ 2 ) are sets of type substitutions; their application to a type 𝑡 is defined as 𝑡 Σ = def 𝜎 ∈Σ 𝑡𝜎. Since they cannot be directly guessed, they are given by the annotation. Subsumption instead is embedded in two type operators. A first operator, dom(), computes the domain of the arrow and is used to check that the application is well-typed. A second type operator, •, computes the type of the result of the application. These type operators are defined as follows:

dom(𝑡) = def max{𝑢 | 𝑡 ≤ 𝑢 → 1} and 𝑡 • 𝑠 = def min{𝑢 | 𝑡 ≤ 𝑠 → 𝑢}. [×E 1 -Alg] Γ ⊢ A [𝜋 1 x | 𝜋 (Σ)] : 𝝅 1 (𝑡) 𝑡 = Γ(x)Σ 𝑡 ≤ (1×1) [×E 2 -Alg] Γ ⊢ A [𝜋 2 x | 𝜋 (Σ)] : 𝝅 2 (𝑡) 𝑡 = Γ(x)Σ 𝑡 ≤ (1×1)
The rules for projections [×E 1 -Alg] and [×E 2 -Alg] follow the same idea as the rule for application [→E-Alg], with the use of two type operators

𝝅 1 (𝑡) = def min{𝑢 | 𝑡 ≤ 𝑢×1} and 𝝅 2 (𝑡) = def min{𝑢 | 𝑡 ≤ 1 × 𝑢}. All these type operators can be effectively computed (cf. Appendix F). [×I-Alg] Γ ⊢ A [(x 1 , x 2 ) | (𝜌 1 , 𝜌 2 )] : 𝑡 1 × 𝑡 2 𝑡 1 = Γ(x 1 )𝜌 1 , 𝑡 2 = Γ(x 2 )𝜌 2
To type a pair (x 1 , x 2 ) it is not necessary to instantiate Γ(x 1 ) or Γ(x 2 ). However, to avoid unwanted correlations, it is necessary to rename the polymorphic type variables of its components. For instance, when typing the pair (x, x) with x : 𝛼 → 𝛼, it is better to type it with (𝛼 → 𝛼, 𝛽 → 𝛽) rather than (𝛼 → 𝛼, 𝛼 → 𝛼), since the former type has strictly more instances than the latter.

40:15 [∈ 1 -Alg] Γ ⊢ A [(x∈𝜏) ? x 1 : x 2 | ∈ 1 (Σ)] : Γ(x 1 ) Γ(x)Σ ≤ 𝜏
To type type-cases, the annotation indicates which of the three rules must be applied (here [∈ 1 ]) and how to instantiate the polymorphic type variables occurring in the type of the tested expression, so that it satisfies the side condition of the applied rule (see also [∈ 2 -Alg] and[0

-Alg] in Appendix G). [Bind 1 -Alg] Γ ⊢ A [𝜅 | k] : 𝑡 Γ ⊢ A [bind x = 𝑎 in 𝜅 | skip k] : 𝑡 x ∉ dom(Γ)
In rule [Bind 1 -Alg] the annotation indicates to skip the definition of the current binding. This rule is used when the binding variable is not required for typing the body 𝜅 under the current context Γ. For instance, this is the case when x only appears in a branch of a typecase that cannot be taken under Γ. The side condition 𝑥 ∉ Γ prevents a potential unsound name conflict between binding variables: as occurrences of x in 𝜅 denote the x binding variable that is being skipped, having the type of a former binding variable x in our environment when typing 𝜅 would be unsound.

[Bind 2 -Alg] Γ ⊢ A [𝑎 | a] : 𝑠 (∀𝑖 ∈ 𝐼 ) Γ, x : 𝑠 ∧ u 𝑖 ⊢ A [𝜅 | k 𝑖 ] : 𝑡 𝑖 Γ ⊢ A [bind x = 𝑎 in 𝜅 | keep (a, {(u 𝑖 , k 𝑖 )} 𝑖 ∈𝐼 )] : 𝑖 ∈𝐼 𝑡 𝑖 𝑖 ∈𝐼 u 𝑖 ≃ 1
This rule tries to type the bound atom and then decomposes its type according to the annotation. This decomposition corresponds to an application of the [∨] rule of the declarative type system with the only difference that the type 𝑠 of the atom is split in several summands by intersecting it with the various u 𝑖 (instead of just two summands as in the rule [∨]) whose union covers 1.

Finally, two annotations indicate when and how to apply rule [∧] to atoms and canonical forms:

[∧-Alg] (∀𝑖 ∈ 𝐼 ) Γ ⊢ A [𝑎 | a 𝑖 ] : 𝑡 𝑖 Γ ⊢ A [𝑎 | ({a 𝑖 } 𝑖 ∈𝐼 )] : 𝑖 ∈𝐼 𝑡 𝑖 𝐼 ≠ ∅ [∧-Alg] (∀𝑖 ∈ 𝐼 ) Γ ⊢ A [𝜅 | k 𝑖 ] : 𝑡 𝑖 Γ ⊢ A [𝜅 | ({k 𝑖 } 𝑖 ∈𝐼 )] : 𝑖 ∈𝐼 𝑡 𝑖 𝐼 ≠ ∅
An expression 𝑒 is typable if and only if its unique (modulo ≡ 𝜅 ) MSC-form is typable, too:

Theorem 3.4 (Soundness and Completeness). For every term 𝑒 of the source language

⊢ 𝑒 : 𝑡 ⇒ ∃k ⊢ A [MSC(𝑒) | k] : 𝑡 ′ ≤ 𝑡 (completeness) ⊢ 𝑒 : 𝑡 ⇐ ⊢ A [MSC(𝑒) | k] : 𝑡 (soundness)
It is easy to generate the unique MSC-form associated to a source language expression 𝑒 (cf. Appendix E). Theorem 3.4 states that this MSC-form is typable if and only if 𝑒 is: we reduced the problem of typing 𝑒 to the one of finding an annotation that makes the unique MSC-form of 𝑒 typeable with the algorithmic type system.8 Figure 3 gives an example of an MSC-form and two possible annotations for it. The term "𝜆𝑥 .(𝑓 𝑥 ∈Int) ? 𝑔(𝑓 𝑥) : 𝑥" (where 𝑓 : ∀𝛼 .𝛼→𝛼 and 𝑔 : Int→Int) is put in MSC-form (on the left). In the first annotation, the function is typed with a single 𝜆 annotation (line 3). The interesting part is the annotation of the binding for u (line 5): the corresponding keep annotation represents an application of the union elimination rule on the occurrences of the expression 𝑓 𝑥 whose type 𝜷 is split into 𝜷∧Int (line 6) and 𝜷∖Int (line 9). Each subcase is annotated accordingly. Notice in the second subcase that the annotation for v is skip (line 10) which indicates that this particular variable must not be used (as 𝑔(𝑓 𝑥) cannot be typed since in the "else" branch, 𝑓 𝑥 has type ¬Int). A different annotation, yielding a better type, is the

Γ = {𝑓 : 𝛼 → 𝛼, 𝑔 : Int → Int} 1 bind z = keep( keep( 2 ({ 3 𝜆𝑥 . 𝜆(𝜷, 𝜆(Int, 𝜆(𝜷 ∖ Int, 4 bind x = 𝑥 in keep(∅, {(1, keep(∅, {(1, keep(∅, {(1, 5 bind u = 𝑓 x in keep(@({{𝛼 𝜷 }}, {∅}), keep(@({{𝛼 Int}}, keep(@({{𝛼 𝜷 ∖ Int}}, 6 7 8 9 10 bind v = g u in bind w = (u∈Int) ? v : x in w {(Int, keep(@({∅}, {∅}), {(1, keep (∈ 1 ({∅}), {(1, ∅)}))}), (¬Int, skip (
{∅}), {(1, keep(@({∅}, {∅}), {(1, keep(∈ 1 ({∅}), {(1, ∅)}))}))}))} {∅}), {(1, skip( keep(∈ 2 ({∅}), {(1, ∅)})))}))} 11 keep (∈ 2 ({∅}), {(1, ∅)}))} 12 ))})), }), 13 in z {(1, ∅)}) {(1, ∅)})
Final Type: The example in Figure 3 also shows why the condition of maximal sharing for our forms is necessary, not only for their uniqueness, but also for the completeness of the algorithmic system: if the two occurrences of 𝑓 𝑥 in "𝜆𝑥 .(𝑓 𝑥 ∈Int) ? 𝑔(𝑓 𝑥) : 𝑥" were not bound by the same variable (as in the leftmost column of line 5 in Figure 3), viz., if the sharing were not maximal, then it would not be possible to deduce that 𝑔(𝑓 𝑥) is well typed: 𝑔 expects an integer, but without maximal sharing it is not possible to deduce that the occurrence of 𝑓 𝑥 in the first branch is indeed of type Int.

𝜷 → 𝜷 ∨ Int (Int → Int) ∧ ((𝜷∖Int) → (𝜷∖Int))
The problem of inferring an annotation for an MSC-form as the above-in particular the rightmost (more precise) annotation in Figure 3-is tackled in the next section.

RECONSTRUCTION

This section describes an algorithm to find an annotation for an expression in MSC-form, such that the pair expression and annotation is typable in the algorithmic system. Though this algorithm is not complete, it is sound and terminating (see Section 4.4 for the formal statements and a discussion about incompleteness). Experimental results are presented in Section 5.

The annotation reconstruction algorithm is composed of two systems of deduction rules: the main reconstruction algorithm (Section 4.2) which produces intermediate annotations containing information about the domains of 𝜆-abstractions and the type decompositions to use in bindings, and the auxiliary reconstruction algorithm (Section 4.3) which converts these intermediate annotations into annotations for the algorithmic type system, by computing instantiations Σ for destructors.

The Tallying Algorithm

One key ingredient used by the reconstruction algorithm is the tallying algorithm. Roughly, tallying is the equivalent of the unification used in algorithm W [START_REF] Damas | Principal Type-Schemes for Functional Programs[END_REF], but for a type system with subtyping. The tallying algorithm was introduced by [START_REF] Castagna | Polymorphic functions with set-theoretic types. Part 2: local type inference and type reconstruction[END_REF] to solve the following problem: given a set of pairs {(𝑡 𝑖 , 𝑡 ′ 𝑖 )} 𝑖 ∈𝐼 and a set of type variables Δ representing the monomorphic type variables, find all substitutions 𝜎 whose domain is disjoint from Δ (noted 𝜎#Δ) and that satisfy ∀𝑖 ∈ 𝐼 . 𝑡 𝑖 𝜎 ≤ 𝑡 ′ 𝑖 𝜎. [START_REF] Castagna | Polymorphic functions with set-theoretic types. Part 2: local type inference and type reconstruction[END_REF] show that this problem is decidable and give an algorithm to characterize all solutions. As for unification, for each instance of the tallying problem there is either no solution or several substitutions each of which is a solution of the problem. The difference is that while with unification all solutions are characterized by a principal substitution, with tallying they are characterized by a principal finite set of substitutions. 9 More precisely, all substitutions that are solutions to a tallying instance are characterized by a principal set Σ of substitutions, such that every 𝜎 ∈ Σ is a solution, and for any solution 𝜎, we have ∃𝜎 1 ∈Σ. ∃𝜎 2 . 𝜎 2 #Δ and 𝜎 ≃ 𝜎 2 • 𝜎 1 , where • denotes the composition of substitutions and ≃ is pointwise type equivalence.

In this work, all tallying instances use a single constraint, and we will note tally({𝑡 1 ≤ 𝑡 2 }) the set of substitutions Σ characterizing all the solutions of the tallying instance {(𝑡 1 , 𝑡 2 )}, where Δ = V 𝑀 and thus ∀𝜎 ∈ Σ. dom(𝜎) ⊆ V 𝑃 (we use the symbol ≤, rather than ≤ to stress that it denotes a constraint to be solved, rather than a pair in the subtyping relation).

The tallying function tally() finds substitutions for polymorphic type variables, but in order to infer the domain of 𝜆-abstractions, we may need to find substitutions for monomorphic type variables. We thus introduce an additional tallying function, tally_infer({𝑡 1 ≤ 𝑡 2 }):

Definition 4.1. Let 𝜎
𝑋 denote the restriction of the substitution 𝜎 to the domain 𝑋 . We define

tally_infer({𝑡 1 ≤ 𝑡 2 }) = {(𝜎 • 𝜎 ′ • 𝜙) V 𝑀 | 𝜎 ′ ∈ tally({fresh(𝑡 1 )𝜙 ≤ fresh(𝑡 2 )𝜙 })}
where fresh(𝑡) denotes the type 𝑡 where polymorphic type variables have been substituted by fresh ones; 𝜙 is a renaming from (vars(𝑡 1 ) ∪ vars(𝑡 2 )) ∩ V 𝑀 to fresh polymorphic variables; and 𝜎 is a substitution mapping polymorphic variables appearing in the image of 𝜎 ′ • 𝜙 to fresh monomorphic variables.

In a nutshell, polymorphic type variables in 𝑡 1 and 𝑡 2 are refreshed in order to decorrelate them, and monomorphic type variables are generalized using 𝜙 so that tally() is allowed to find solutions for them. Each solution 𝜎 ′ is composed with 𝜙 in order to restore the connection with the initial monomorphic type variables, and the polymorphic type variables in the image of the resulting substitution are transformed into monomorphic ones by composing 𝜎 with it. Finally, the substitution is restricted to V 𝑀 (i.e., to the domain of 𝜙).

For example, an instance such as tally_infer({Int∧𝛼 → Int∧𝛼 ≤ 𝜷 → 𝛼 }) can be generated during reconstruction, when a function of type Int∧𝛼 → Int∧𝛼 is applied to an argument of type 𝜷, but the 𝛼 on the right-hand of ≤ is unrelated to the one on the left-hand side. Decorrelating them yields a unique solution {𝜷 𝜷 ′ ∧Int}, that is, 𝜷 must be substituted by 𝜷 ′ ∧Int in our context for the application to be typeable.

Main Reconstruction Algorithm

The main reconstruction algorithm, defined in this section, infers the domains of 𝜆-abstractions and the decompositions of types into disjoint unions to use for bindings. It works by successively refining intermediate annotations defined below. These intermediate annotations store information about the domains of 𝜆-abstractions and the decompositions of bindings. However, the instantiations Σ used to type destructors (i.e., applications, projections, and typecases) in the algorithmic type system are not stored in intermediate annotations, because they might get invalidated as the reconstruction progresses: when new information is found about the domain of a lambda or the decomposition of a binding, the algorithm will retype some intermediate definitions of the MSCform, thus invalidating the instantiations Σ of later definitions. Thus, these instantiations Σ will be 9 This is due to the presence of the empty type. For instance, the principal solution of unifying 𝛼 × 𝛽 with 𝑠 × 𝑡 is the substitution {𝛼 𝑠, 𝛽 𝑡 }, while all substitutions that make the former type become a subtype of the latter are characterized by a set containing three distinct substitutions: {𝛼 0}, {𝛽 0}, and {𝛼 𝑠, 𝛽 𝑡 }.

recomputed whenever needed, using the auxiliary system (Section 4.3) that converts intermediate annotations into annotations for the algorithmic type system. Atom and form intermediate annotations are defined by the grammar below:

Split annotations S ::= {(u, K), . . . , (u, K)} Atom intermediate annot. A ::= infer | untyp | typ | ({A, . . . , A}, {A, . . . , A}) | ∈ 1 | ∈ 2 | 𝜆(u, K) Form intermediate annot. K ::= infer | untyp | typ | ({K, . . . , K}, {K, . . . , K}) | try-skip (K) | try-keep (A, K, K) | propagate (A, L , S, S) | skip (K) | keep (A, S, S)
In the following, we use the metavariable 𝜂 to range over both atoms and expressions (i.e., 𝜂 ::= 𝑎 | 𝜅).

Similarly, the metavariable h ranges over atom annotations a and form annotations k (i.e., h ::= a | k); while the metavariable H ranges over atom intermediate annotations A and form intermediate annotations

K (i.e. H ::= A | K).
Let 𝜓 range over monomorphic substitution, that is, substitutions from V 𝑀 to monomorphic types, and Ψ range over finite sets of monomorphic substitutions (Ψ ::= {𝜓, . . . ,𝜓 }). The main reconstruction algorithm is presented as a deduction rule system, for judgments of the form

Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ R,
where R is a result defined as follows:

Result R ::= Ok(H ) | Fail | Split(Γ, H, H ) | Subst(Ψ, H, H ) | Var (x, H, H )
Let us see what each result for Γ ⊢ * R ⟨𝜂 | H ⟩ means: Ok(H ′ ): the reconstruction was successful and 𝜂 can be typed by the algorithmic type system using the annotation H ′ (after converting it into an annotation h using the auxiliary reconstruction system). This result is terminal (i.e., it is a definitive answer that cannot be further refined). Fail: the reconstruction has failed. The algorithm was not able to find an annotation that makes 𝜂 typable with the algorithmic system. This result is terminal. Subst(Ψ, H 1 , H 2 ): the reconstruction found a set of substitutions Ψ that if applied to Γ may make 𝜂 typable. In practice, for each substitution 𝜓 ∈ Ψ, the reconstruction will be called again on the environment Γ𝜓 and annotation H 1 𝜓 . However, this does not necessarily mean that the reconstruction will fail on the current environment Γ: 𝜂 might still be typeable but with a less precise type (e.g., it could yield an arrow type with a smaller domain). Thus, this default case which does not instantiate Γ is also explored, using the annotation H 2 instead of H 1 . Split(Γ ′ , H 1 , H 2 ): the reconstruction found some splits for the variables in dom(Γ ′ ) that if applied to Γ may make 𝜂 typable. In practice, the system generates several new environments: one is obtained by (pointwise) intersecting Γ with Γ ′ and then it is used to retype 𝜂 with the annotation H 1 ; the others are obtained by intersecting Γ with all the possible pointwise negations of Γ ′ and then they are used to retype 𝜂 with the annotation H 2 . Var (x, H 1 , H 2 ): the reconstruction found that in order to type 𝜂, the definition of the bindabstracted variable x should be typed. Any branch that successfully types it continues with the annotation H 1 , otherwise it continues with the annotation H 2 .

Initially, any form or atom 𝜂 is annotated with infer, and this annotation is then refined until it yields a terminal result (i.e., either Ok() or Fail). The rules below are presented by decreasing priority (i.e., the first rule that applies is used). Some rules have been omitted for concision, but the reader can find the full reconstruction system in Appendix H.1. There are two different forms of judgments:

Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ R and Γ ⊢ R ⟨𝜂 | H ⟩ ⇒ R.
We first define rules for the judgment ⊢ R for every canonical form and atom. The results of these judgments are not necessarily terminal and, therefore, it may be necessary to call the reconstruction again in order to refine them. This is the purpose of ⊢ * R judgments which call repetitively ⊢ R judgments when relevant, so that in the end we get a terminal result. Let us first focus on ⊢ R judgments.

[Ok]

Γ ⊢ R ⟨𝜂 | typ⟩ ⇒ Ok(typ) [Fail] Γ ⊢ R ⟨𝜂 | untyp⟩ ⇒ Fail
If a canonical form or atom 𝜂 is annotated with typ, then reconstruction is finished for 𝜂, and it is typeable in the current context Γ. The annotation typ is never used on 𝜆-abstractions and bindings because the system needs to store more information for them. Likewise, if a form or atom 𝜂 is annotated with untyp, then reconstruction is finished for 𝜂 by failing in the current context.

[AxOk]

𝑥 ∈ dom(Γ) Γ ⊢ R ⟨𝑥 | infer⟩ ⇒ Ok(typ) [AxFail] Γ ⊢ R ⟨𝑥 | infer⟩ ⇒ Fail
If a 𝜆-abstracted variable 𝑥 is in the environment, then it is typeable and thus the algorithm returns Ok(typ). Otherwise, 𝑥 is undefined and Fail is returned.

[AppVar 𝑖 ] x 𝑖 ∉ dom(Γ) Γ ⊢ R ⟨x 1 x 2 | infer⟩ ⇒ Var (x 𝑖 , infer, untyp)
To type the application x 1 x 2 , we must first ensure that {x 1 , x 2 } ⊆ dom(Γ). If it is not the case, then the two rules [AppVar 𝑖 ] (for 𝑖 = 1, 2) try to remedy it by returning Var (x 𝑖 , infer, untyp), which is the result that asks the system to try to type the atom bound to x 𝑖 for x 𝑖 ∉ dom(Γ). If the attempt is successful, then the algorithm will continue the reconstruction for the application with the annotation infer and x 𝑖 ∈ dom(Γ), otherwise it will continue with the annotation untyp making the reconstruction fail on this application.

[AppInfer] [AppInfer] tries to find all instances of the current context in which the application x 1 x 2 is typeable, by subsuming Γ(x 1 ) (the type of the function) to Γ(x 2 )→𝛼 (a function type whose domain is the type of the argument). For that, it calls the tallying algorithm which returns a set of substitutions Ψ. Then, Subst(Ψ, typ, untyp) is returned, meaning that this application should be typeable under every instance Γ𝜓 of the current context Γ (with 𝜓 ∈ Ψ). The default case (i.e., when the current context is unchanged, for example, when Ψ = ∅) cannot be typed, so it is annotated with untyp (see rule [Iterate 2 ] later on). The rules for pairs are similar and have been omitted.

Ψ = tally_infer({Γ(x 1 ) ≤ Γ(x 2 ) → 𝛼 }) Γ ⊢ R ⟨x 1 x 2 | infer⟩ ⇒ Subst(Ψ, typ, untyp) 𝛼 ∈ V 𝑃 fresh If {x 1 , x 2 } ⊆ dom(Γ), then the rule
[CaseSplit]

Γ(x) ≰ 𝜏 Γ(x) ≰ ¬𝜏 Γ ⊢ R ⟨(x∈𝜏) ? x 1 : x 2 | infer⟩ ⇒ Split({(x : 𝜏)}, infer, infer)
The key rule for type-cases is [CaseSplit], corresponding to the case where x is in Γ, but with a type that does not allow the selection of a specific branch. Thus, we need to partition the type of x in two, one part being a subtype of 𝜏 and the other a subtype of ¬𝜏. This is achieved by returning Split({(x : 𝜏)}, infer, infer): this result is backtracked up to the binding of x, where it will split the associated type, accordingly.

[CaseThen]

Γ(x) ≤ 𝜏 Ψ = tally_infer({Γ(x) ≤ 0}) Γ ⊢ R ⟨(x∈𝜏) ? x 1 : x 2 | infer⟩ ⇒ Subst(Ψ, typ, ∈ 1 ) [CaseElse] Γ(x) ≤ ¬𝜏 Ψ = tally_infer({Γ(x) ≤ 0}) Γ ⊢ R ⟨(x∈𝜏) ? x 1 : x 2 | infer⟩ ⇒ Subst(Ψ, typ, ∈ 2 ) [CaseVar 𝑖 ] x 𝑖 ∉ dom(Γ) Γ ⊢ R ⟨(x∈𝜏) ? x 1 : x 2 | ∈ 𝑖 ⟩ ⇒ Var (x 𝑖 , typ, untyp)
When the type of x allows the selection of a branch, then either the rule [CaseThen] or the rule [CaseElse] applies. If we are in the case of [CaseThen], that is Γ(x) ≤ 𝜏, then we have to determine whether we will apply the algorithmic rule [0-Alg] or the algorithmic rule [∈ 1 -Alg]. To determine it, the [CaseThen] rule calls tally_infer({Γ(x) ≤ 0}) which returns the set of contexts Γ𝜓 (for 𝜓 ∈ Ψ) under which the algorithmic rule [0-Alg] is to be applied, that is, the contexts under which the tested expression x has an empty type. The default case, corresponding to the case in which the type of Γ(x) is not guaranteed to be empty and, thus, in which the algorithmic rule [∈ 1 -Alg] must be applied, is annotated with ∈ 1 . This annotation is handled by the rule [CaseVar 1 ] which forces the system to type x 1 , the binding variable associated to the first branch. The case for [CaseElse] and [CaseVar 2 ] is analogous.

We omitted the remaining rules for type-cases since they are straightforward: the rule for x ∉ dom(Γ), which triggers a Var (x, infer, untyp) result; two rules similar to [CaseVar 𝑖 ], but where x 𝑖 ∈ dom(Γ), which simply return Ok(typ).

[LambdaInfer]

Γ ⊢ R ⟨𝜆𝑥 .𝜅 | 𝜆(𝜶 , infer)⟩ ⇒ R Γ ⊢ R ⟨𝜆𝑥 .𝜅 | infer⟩ ⇒ R 𝜶 ∈ V 𝑀 fresh [Lambda] Γ, 𝑥 : u ⊢ * R ⟨𝜅 | K⟩ ⇒ R Γ ⊢ R ⟨𝜆𝑥 .𝜅 | 𝜆(u, K)⟩ ⇒ map(𝑋 ↦ → 𝜆(u, 𝑋 ), R)
The rules for 𝜆-abstractions mimic algorithm W. Rule [LambdaInfer] transforms the initial infer annotation into a 𝜆(𝜶 , infer) annotation. As in W, 𝜆-abstracted variables are initially given a fresh type variable, which will then be substituted as needed while reconstructing the type of the body; here we use a fresh monomorphic variable, but tally_infer() will transform it into a polymorphic-thus, instantiable-one, just for the reconstruction in the body. Rule [Lambda] adds the 𝜆-abstracted variable to the environment with the type specified in the annotation, recursively calls reconstruction on the body, and reestablishes the variable type annotation on the result. The notation map(𝑋↦ →𝑓 (𝑋 ), R) denotes the result R where 𝑓 has been applied to every annotation 𝑋 .

[BindInfer]

Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-skip (infer)⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | infer⟩ ⇒ R
The [BindInfer] rule transforms an initial infer annotation into a try-skip (infer) annotation which skips the binding and annotates the body 𝜅 with infer. We do not try to type the definition of a binding until it is actually used, because its variable might appear only in unreachable positions (e.g., in an unreachable branch of a type-case). In other words, we implement a lazy typing discipline for bind-abstracted variables. If the variable is used at some point, then an attempt to type it will be initiated by the [BindTrySkip 1 ] rule below:

[BindTrySkip 1 ] Γ ⊢ * R ⟨𝜅 | K⟩ ⇒ Var (x, K 1 , K 2 ) Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-keep (infer, K 1 , K 2 )⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-skip (K)⟩ ⇒ R
This rule tries to type the body of the binding, starting with the annotation K (initially, infer). If the result is Var (x, K 1 , K 2 ), then it means that the current binding is used in the body 𝜅 and, thus, the system should try to type it. Consequently, the annotation for the current binding is changed into a try-keep (infer, K 1 , K 2 ) so that, at the next iteration, its definition will be reconstructed.

If typing the body of the binding yields a result different from Var (x, K 1 , K 2 ), then this result is just propagated as in [Lambda] (the corresponding rules have been omitted).

[BindTryKeep 1 ] Γ ⊢ * R ⟨𝑎 | A⟩ ⇒ Ok(A ′ ) Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | keep (A ′ , {(1, K 1 )}, ∅)⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-keep (A, K 1 , K 2 )⟩ ⇒ R [BindTryKeep 2 ] Γ ⊢ * R ⟨𝑎 | A⟩ ⇒ Fail Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | skip (K 2 )⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-keep (A, K 1 , K 2 )⟩ ⇒ R
As expected, if the current annotation for the binding is a try-keep (A, K 1 , K 2 ), then the system tries to reconstruct the annotation for the definition. If it succeeds, then it becomes possible to type the definition and to continue the reconstruction of the body using K 1 . This is what [BindTryKeep 1 ] does by changing the current annotation to keep (A ′ , {(1, K 1 )}, ∅) (more details below). If the reconstruction of the definition fails (rule [BindTryKeep 2 ]), then we have no choice but to skip this definition and use the default annotation K 2 to type the body.

1

Γ ⊢ E (𝑎 : ¬(u ∖ Γ ′ (x))) ⇒ L 2 Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | keep (A, {(u, K)} ∪ S, S ′ )⟩ ⇒ Split(Γ ′ ∖ x, K ′ 1 , K ′ 2 ) with, in the last rule, K ′ 1 = propagate (A, L 1 ∪ L 2 , {(u ∧ Γ ′ (x), K 1 ), (u ∖ Γ ′ (x), K 2 )} ∪ S, S ′ ) and K ′ 2 = keep (A, {(u, K 2 )} ∪ S, S ′ )
In both rules, the definition of the binding is typed using the annotation A. For that, it is first converted into an annotation a of the algorithmic type system, using the deduction rules for the judgment Γ ⊢ P ⟨𝑎 | A⟩ ⇒ a, defined in Section 4.3. Then, the type 𝑠 obtained for the definition is intersected with one of the parts of the type decomposition, according to the second argument of the keep() annotation (i.e., {(u, K)} ∪ S in both rules), and the corresponding annotation for the body is reconstructed recursively. Note that, since split annotations are sets, then the order in which the parts are explored is arbitrary.

The rule [BindKeep 1 ] for an annotation keep (A, S, S ′ ) is responsible for moving a branch from S to S ′ when the result for the branch is Ok(). If instead the reconstruction of the body requires to further split the type of x, then the rule [BindKeep 2 ] splits the current branch into two branches. However, before exploring these two branches, some information about the split needs to be propagated, to ensure that when a split is explored, it is under a context as precise as possible.

Let us explain this by an example. Assume we have a polymorphic primitive function id of type 𝛼 → 𝛼 and an initial environment Γ = {𝑥 : Bool}. We want to type the following canonical form, and deduce for it the type True (since x and y are always bound to the same value): bind x = 𝑥 in bind y = id x in bind z = (y∈True) ? x : true in z At some point, the partition associated to y will change from {1} to {True, ¬True} because of the type-case (rule [CaseSplit]). However, if the case corresponding to (y : True) is immediately explored, it will yield for the body the type Bool, because x still has the type Bool in the environment. In order to obtain the more precise type True, we must deduce, before exploring the case (y : True), that when id x (the definition of y) has type True, then x also has type True. Knowing that, the type of x should be split accordingly into {True, ¬True}.

This mechanism of backward propagation of splits is initiated in the [BindKeep 2 ] rule with the two premises Γ ⊢ E (𝑎 :

¬(u ∧ Γ ′ (x))) ⇒ L 1 and Γ ⊢ E (𝑎 : ¬(u ∖ Γ ′ (x))) ⇒ L 2 .
This auxiliary judgment Γ ⊢ E (𝑎 : u) ⇒ L , defined in Appendix H.3, can be read as follows: refining the current environment Γ with one of the Γ ′ ∈

L

ensures that the atom 𝑎 will have type u. The refinements we obtain are stored in the annotation of the binding, using an annotation propagate (A, L , S, S ′ ). This annotation is handled by two other rules (omitted here) whose role is to propagate these refinements one after the other using successive Split(Γ ′ , K 1 , K 2 ) results (with Γ ′ ∈ L ), before finally restoring a keep (A, S, S ′ ) annotation.

[InterEmpty]

Γ ⊢ R ⟨𝜂 | (∅, ∅)⟩ ⇒ Fail [InterOk] Γ ⊢ R ⟨𝜂 | (∅, 𝑆)⟩ ⇒ Ok( (∅, 𝑆)) [Inter 1 ] Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ Ok(H ′ ) Γ ⊢ R ⟨𝜂 | (𝑆, {H ′ } ∪ 𝑆 ′ )⟩ ⇒ R Γ ⊢ R ⟨𝜂 | ({H } ∪ 𝑆, 𝑆 ′ )⟩ ⇒ R [Inter 2 ] Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ Fail Γ ⊢ R ⟨𝜂 | (𝑆, 𝑆 ′ )⟩ ⇒ R Γ ⊢ R ⟨𝜂 | ({H } ∪ 𝑆, 𝑆 ′ )⟩ ⇒ R [Inter 3 ] Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ R Γ ⊢ R ⟨𝜂 | ({H } ∪ 𝑆, 𝑆 ′ )⟩ ⇒ map(𝑋 ↦ → ( ({𝑋 } ∪ 𝑆, 𝑆 ′ )), R)
Intersection annotations are introduced by the ⊢ * R judgments defined below. In an intersection annotation (𝑆, 𝑆 ′ ), the annotations in 𝑆 ′ are fully processed (i.e., the associated reconstruction returned Ok()), while the annotations in 𝑆 are not: they still have to be refined one after the other (rule [Inter 3 ]). If one of them becomes fully processed, it is moved in 𝑆 ′ (rule [Inter 1 ]). Conversely, if one of them fails, it is removed (rule [Inter 2 ]). The process stops when 𝑆 is empty: then, the reconstruction fails if 𝑆 ′ is empty (rule [InterEmpty]), and succeed otherwise (rule [InterOk]).

40:23

Finally, we formalize the rules for the judgments ⊢ * R . As said earlier, the purpose of ⊢ * R is to repeatedly call ⊢ R judgments so that, in the end, we obtain a terminal result.

[Iterate 1 ] Γ ⊢ R ⟨𝜂 | H ⟩ ⇒ Split(Γ ′ , H 1 , H 2 ) Γ ⊢ * R ⟨𝜂 | H 1 ⟩ ⇒ R Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ R Γ ′ = ∅ [Iterate 2 ] Γ ⊢ R ⟨𝜂 | H ⟩ ⇒ Subst({𝜓 𝑖 } 𝑖 ∈𝐼 , H 1 , H 2 ) Γ ⊢ * R ⟨𝜂 | ({H 1 𝜓 𝑖 } 𝑖 ∈𝐼 ∪ {H 2 }, ∅)⟩ ⇒ R Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ R ∀𝑖 ∈ 𝐼 . 𝜓 𝑖 #Γ
The iteration continues as long as it yields non-terminal results that are immediately usable, that is, either they return a trivial split (i.e., Γ ′ = ∅) as in rule

Γ ⊢ R ⟨𝜂 | H ⟩ ⇒ R Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ R In particular, if R = Split(Γ ′ , H 1 , H 2 ) where Γ ′ ≠ ∅ (i.e., [Iterate 1 ] does not apply), then [Stop] backtracks until Γ ′ becomes empty; likewise if R = Subst({𝜓 𝑖 } 𝑖 ∈𝐼 , H 1 , H 2 )
and Γ𝜓 𝑖 Γ for some 𝑖 (i.e. [Iterate 2 ] does not apply), then [Stop] backtracks until it exits the scope of the binders of the variables that make the side condition of [Iterate 2 ] fail.

Auxiliary Reconstruction Algorithm

The auxiliary reconstruction algorithm defined in this section converts an intermediate annotation of the main reconstruction system into an annotation for the algorithmic type system. For that, it needs to retrieve the polymorphic substitutions Σ needed to type the atoms.

Formally, the algorithm takes as input an environment Γ, an atom or canonical form 𝜂, and an intermediate annotation H , and produces an annotation h for the algorithmic type system. It is presented as a deduction rule system for judgments of the form Γ ⊢ P ⟨𝜂 | H ⟩ ⇒ h. Some rules have been omitted for concision (they can be found in Appendix H.2): for instance, the rules for constants and axioms are omitted since straightforward, as they just transform an intermediate annotation typ into an annotation ∅ for the algorithmic type system; likewise, the rules for 𝜆-abstractions and intersections are straightforward and have been omitted, since they just proceed recursively on their children annotations. The most important rule for this system is the one for applications:

[App] 𝑡 1 = Γ(x 1 ) 𝑡 2 = Γ(x 2 ) 𝜌 1 = refresh(𝑡 1 ) 𝜌 2 = refresh(𝑡 2 ) Σ = tally({𝑡 1 𝜌 1 ≤ 𝑡 2 𝜌 2 → 𝛼 }) Γ ⊢ P ⟨x 1 x 2 | typ⟩ ⇒ @({𝜎 • 𝜌 1 | 𝜎 ∈ Σ}, {𝜎 • 𝜌 2 | 𝜎 ∈ Σ}) Σ ≠ ∅ 𝛼 ∈ V 𝑃 fresh
where refresh(𝑡) returns a renaming from vars(𝑡) ∩ V 𝑃 to fresh polymorphic variables.

For applications, an annotation of the form @(Σ 1 , Σ 2 ) must be produced. In order to find some instantiations Σ 1 and Σ 2 (for x 1 and x 2 respectively) that make the application typable, the [App] rule solves the tallying instance tally({𝑡 1 𝜌 1 ≤ 𝑡 2 𝜌 2 → 𝛼 }). The purpose of 𝜌 1 and 𝜌 2 is to decorrelate type variables in Γ(x 1 ) and in Γ(x 2 ). For instance, assume we want to reconstruct the instantiations for the atom "x x" with Γ(x) = 𝛽 → 𝛽. The tallying instance tally({𝛽 → 𝛽 ≤ (𝛽 → 𝛽) → 𝛼 }) yields only a very specific, uninteresting solution (i.e., 𝛼 = 𝛽 = 𝜇𝑋 .𝑋 → 𝑋 )10 because of the use of the same type variable 𝛽 on both sides of ≤. But each occurrence of x has a polymorphic type that can be instantiated independently. Thus, we remove this useless and constraining dependency by refreshing the generic type variables yielding tally({𝛽 ′ → 𝛽 ′ ≤ (𝛽 → 𝛽) → 𝛼 }) which has interesting solutions, in particular {𝛽 ′ 𝛽 → 𝛽 ; 𝛼 𝛽 → 𝛽}. The side-condition Σ ≠ ∅ ensures that the tallying instance has at least one solution (otherwise the annotation produced would be invalid). The rules for projections, pairs, and type-cases are similar and, thus, omitted.

[BindKeep]

Γ ⊢ P ⟨𝑎 | A⟩ ⇒ a Γ ⊢ A [𝑎 | a] : 𝑠 (∀𝑖 ∈ 𝐼 ) Γ, x : 𝑠 ∧ u 𝑖 ⊢ P ⟨𝜅 | K 𝑖 ⟩ ⇒ k 𝑖 Γ ⊢ P ⟨bind x = 𝑎 in 𝜅 | keep (A, ∅, {(u 𝑖 , K 𝑖 )} 𝑖 ∈𝐼 )⟩ ⇒ keep (a, {(u 𝑖 , k 𝑖 )} 𝑖 ∈𝐼 ) ( * )
(where ( * ) is 𝑖 ∈𝐼 u 𝑖 ≃ 1). The rule [BindKeep] takes as input an intermediate annotation keep (A, S, S ′ ), with S = ∅, since all branches must have been fully explored by the main reconstruction algorithm. The rule recursively transforms the intermediate annotation A for the definition 𝑎 into an annotation a for the algorithmic type system, and uses it to type 𝑎. It can then update the environment and proceed recursively on the body 𝜅, for each branch in S ′ .

Properties of the Reconstruction Algorithm

As recalled at the beginning of the section, reconstruction is sound, terminating, but incomplete. The incompleteness of the reconstruction algorithm is inherent to our system and derives from the lack of principal typing. A simple example is the curried function map defined in the third row of Table 1 in the next section. Our reconstruction deduces for it the type (𝛼 → 𝛽) → [𝛼 * ] → [𝛽 * ] (actually, a slightly more precise type), where [𝛼 * ] is the type of the lists of elements of type 𝛼. This states that an application of map yields a function that maps lists of 𝛼's into lists of 𝛽's. But for every natural number 𝑛, the declarative system can also deduce that the result maps lists of 𝛼's of length 𝑛 into lists of 𝛽's of the same length 𝑛. Our algorithm can check each of these types, but none of them can be deduced from the type reconstructed by the algorithm. And since we do not have dependent types or infinite intersections, then the declarative system cannot have a principal type expressing all these different derivations. In other terms, incompleteness stems from the fact that the declarative system can use all the infinitely many decompositions of unions in the union elimination rule, and the infinitely many decompositions of the domain of a function when reconstructing its type as an intersection of arrows. The algorithmic counterpart of this, is that there are infinitely many annotations that the algorithmic system can use to type these expressions and that these infinite choices cannot be summarized by a notion of principal annotation: the reconstruction chooses one particular annotation, and therefore it will miss some solutions.

There is a second source of incompleteness for reconstruction, which is not inherent to the system, but a design choice, instead: the fact that reconstruction does not perform the so-called "expansion" of intersection types. This is shown by the rule [App] in Section 4.3, where tally is applied without expanding the types in the constraint (e.g., if tally({𝑡 1 𝜌 1 ≤ 𝑡 2 𝜌 2 → 𝛼 }) fails we can expand the type of the function and try tally({𝑡 1 𝜌 1 ∧ 𝑡 1 𝜌 3 ≤ 𝑡 2 𝜌 2 → 𝛼 }), and so on and so forth by alternating expansions on the function and on the argument types: see [Castagna et al. Table 1. Types inferred by the implementation (times are in ms)

Code

[] -> [] | h :: t -> concat ( flatten h ) ( flatten t ) | _ -> [x] (Tree → [(𝛼∖[1 * ]) * ]) ∧ (𝛽∖[1 * ] → [ 𝛽∖[1 * ] ]) where Tree = [Tree * ] ∨ (𝛼∖[1+]) 374.41
2015, Section 3.2.3] for more details). The consequence of this is that if you take the definition of the function filter given in row 4 of Table 1, and you remove all type annotations, then the type reconstructed by the algorithm for it is less precise than the one specified by the annotations, which could have been reconstructed if the algorithm had instead expanded the type of the parameter f. Despite incompleteness, the declarative rules of Figure 2 form a reliable guide to which programs are accepted, provided we bear in mind that the algorithm approximates data structures according to the tests performed on them. So, typically, the type reconstructed for a function on lists, will probably differentiate the cases for empty and not-empty lists, but not for, say, lists of size 42, unless the function contains an explicit test for it. This (and to a lesser extent, expansion) is essentially the main difference with the declarative system, which has the liberty to deduce the type for the case of lists of size 42, even if this property is not tested in the body of the function. In that case, the programmer can still use an explicit type annotation to check that the specific type works.

IMPLEMENTATION

We have implemented the reconstruction algorithm presented in Section 4, using the CDuce [CDuce ] API for the subtyping and the tallying algorithms. The prototype is 4500 lines of OCaml code and features several extensions such as optional type annotations, pattern matching (cf. Appendix A), records, and a more user-friendly syntax. It implements some optimizations, briefly discussed at the end of this section, for instance memoization and a mechanism to avoid typing redundant branches when inferring the domains of 𝜆-abstractions. We give in Table 1 the code of several functions, using a syntax similar to OCaml, where uppercase identifiers (e.g., True, Truthy) denote types and lowercase identifiers denote variables or constants. For each function we report its inferred type and the time used to infer it. To enhance readability we manually curated the types which, thus, may be syntactically different from (but are semantically equivalent to) the types printed by the prototype. The experiments were performed on an Intel Core i9-10900KF 3.70GHz CPU. The code was compiled natively using OCaml 4.14.1. All these examples (and more) can be tested on the web-based interactive prototype hosted at https://www.cduce.org/dynlang. The web version is compiled to JavaScript using js_of_ocaml [Ocsigen], and is about 8 times slower than the native version. that the elements in 𝛽 do not satisfy the predicate, then the inferred type has these elements removed from the type of the result.

The grammar for expressions in Figure 1 does not include recursive functions, since from a theoretical viewpoint they are useless: Milner [1978, page 356] justifies the addition of a "fix 𝑥 .𝑒" expression by the fact that his system cannot type Curry's fixpoint combinator, but, as explained in Section 1.1 and shown by Code 2 above, our system can. However, from a practical viewpoint, the use of let rec definitions instead of fixed-points combinators may dramatically improve the speed of reconstruction, which is why the previous definitions of map and filter with a fixedpoint combinator must be considered just as stress tests for our reconstruction algorithm. For recursive functions we implemented classic let rec definitions, for which the reconstruction takes the arity of the function into account. Code 5 shows the use of let rec and of pattern matching (cf. Appendix A.3) and is an example of the improvement brought by let rec definitions: reconstruction for the same definition but with a fixpoint combinator is four times slower. The code defines the deep flatten function that transforms arbitrary nested lists into the list of their elements (where concat is a function of type [𝛼 * ] → [𝛽 * ] → [(𝛼 * ) (𝛽 * )], the result being the type of lists starting with 𝛼 elements and ending with 𝛽 ones). [START_REF] Greenberg | The Dynamic Practice and Static Theory of Gradual Typing[END_REF] considers this function to be the ultimate test for any type system: as he explains, this simple polymorphic function defies all type systems since of all existing languages, none can reconstruct a type for it and only a couple of languages can check its explicitly typed version: CDuce and Haskell (the latter by resorting to complex metaprogramming constructions). Our system reconstructs a precise type for flatten as shown by the first arrow in its intersection type, which states that flatten is a function that takes a tree (i.e., either a list of elements that are trees, or a value different from a list) and returns the list of elements of the tree that are not lists; the other arrow of the intersection states that when flatten is applied to an element different from a list, then it returns the list containing only that element.

Our prototype focuses on proximity with the inference system for reconstruction, rather than on performance: we used it mainly to explore and test our system, which is why it is implemented in a purely functional style with persistent data structures (so as to simulate the reconstruction inference rules). Nonetheless, a few optimizations were implemented in order to mitigate the cost of backtracking and branching. One source of inefficiency comes from the intersection nodes that are generated when a destructor is reconstructed. This generation can lead to an explosion of the number of branches to explore, even though many of these branches are redundant. In the prototype, this is mitigated by recording, for each 𝜆-abstraction, the domains already explored for it, and by trimming branches that do not explore new combinations of domains.

40:27

Another source of inefficiency comes from the type decompositions performed after each binding. Although these type decompositions are usually small (e.g., the type of a binding is seldom split in more than two parts), it becomes an issue when typing large expressions with multiple type-cases. For instance, a preliminary and unoptimized implementation of the reconstruction algorithm took about 40 seconds to type the bal(ance) function used in the module Map of the OCaml standard library, that contains 6 different pattern matches and 4 type-cases [OCaml 2023]. Adding a simple memoization mechanism that prevents the reconstruction from retyping an atom several times for equivalent contexts, decreased the inference time down to 4 seconds.

While these simple optimizations significantly improve performance, they are still far from what would be considered acceptable for real applications. To be used in mainstream languages, the type system will have to be adapted and restricted so as to ensure better and uniform performance. To this purpose, we believe that some more language-oriented optimization techniques could be of help. An example is what the development team of Luau [Luau] did on the occasion of its recent switch to semantic subtyping [START_REF] Jeffrey | Semantic Subtyping in Luau[END_REF]]. The developers did this switch by implementing a two-phase approach: first, a sound syntactic system, fast but imprecise, is used to try to prove subtyping, and only if it fails, the computationally expensive semantic subtyping inference is used. We think not only that such a staged approach could be applied in our case, but also that the partial results of the first phase could be used to improve the performance of the later phases, as in the case of the let rec, where knowing the arity of the defined function improves the performance of the reconstruction. This could be further coupled with slicing, meaning that our type reconstruction could be applied to very delimited regions that would bound the possibility of backtracking. These techniques are language-dependent, and quite different from the algorithmic aspects developed here, though they will completely rely on it. We plan to explore them in future work.

RELATED WORK

This work can be seen as a polymorphic extension of [Castagna et al. 2022b] from which it borrows some key notions, such as (𝑖) the combination of the union elimination rule (from [START_REF] Barbanera | Intersection and Union Types[END_REF]) with three rules for type-cases, in order to capture the essence of occurrence typing ( [START_REF] Tobin | The Design and Implementation of Typed Scheme[END_REF]), (𝑖𝑖) the use of MSC forms to drive the application of the union elimination rule, and (𝑖𝑖𝑖) the use of annotations in the algorithmic type system. However, the introduction of polymorphic types greatly modifies the meta-theory. Besides its influence on the union elimination rule, the interplay between intersection, union elimination and instantiation suggests a different style of type annotations, to be amenable to type inference. We use external annotations while [Castagna et al. 2022b] annotates terms. Further, the presence of type variables imposes to use tallying in an inference algorithm inspired by W by [START_REF] Damas | Principal Type-Schemes for Functional Programs[END_REF] and from [START_REF] Castagna | Polymorphic functions with set-theoretic types. Part 2: local type inference and type reconstruction[END_REF], where tallying was first introduced to type polymorphic applications. This yields a clear improvement over [Castagna et al. 2022b] which is unable to infer higher-order types for function arguments, while our algorithm is able to do so even for recursive functions.

The use of trees to annotate calculi with full-fledged intersection types is common. In the presence of explicitly-typed overloaded functions, one must be able to precisely describe how the types of nested 𝜆-abstractions relate to the various "branches" of the outermost function. The work most similar to ours is [START_REF] Liquori | Intersection-types à la Church[END_REF], since the deductions are performed on pairs of marked term and proof term. A marked term is an untyped term where variables are marked with integers and a proof term is a tree that encodes the structure of the typing derivation and relates marks to types. Other approaches, such as [START_REF] Bono | A typed lambda calculus with intersection types[END_REF][START_REF] Ronchi | Intersection Typed lambda-calculus[END_REF][START_REF] Wells | Branching Types[END_REF], duplicate the term typed with an intersection, such that each copy corresponds exactly to one member of the intersection. Lastly, the work of [START_REF] Wells | Branching Types[END_REF] does not duplicate terms but rather decorate 𝜆-abstractions with a richer concept of branching shape which essentially allows one to give names to the various branches of an overloaded function and to use these names in the annotations of nested 𝜆-abstraction. Note that none of these works features type reconstruction, which was our main motivation to eschew annotations within terms, since the backtracking nature of our reconstruction would imply rewriting terms over and over.

Inference for ML systems with subtyping, unions, and intersections has been studied in MLsub [START_REF] Dolan | Polymorphism, Subtyping, and Type Inference in MLsub[END_REF] and extended with richer types and a limited form of negation in MLstruct [START_REF] Parreaux | MLstruct: Principal Type Inference in a Boolean Algebra of Structural Types[END_REF]. Both works trade expressivity for principality. They define a lattice of types and an algebraic subtyping relation that ensures principality, but forbids the intersection of arrow types. This precludes them from expressing overloaded functions, but allows them to define a principal polymorphic type inference with unions and intersections. We justify our choice of set-theoretic types, with no type principality and a complex inference, by our aim to type dynamic languages, such as Erlang or JavaScript, where overloading plays an important role. We favour the expressivity necessary to type many idioms of these languages, and rely on user-defined annotations when necessary to compensate for the incompleteness of type inference. Lastly, both works implement some form of type simplifications (e.g., [START_REF] Dolan | Polymorphism, Subtyping, and Type Inference in MLsub[END_REF] use automata techniques to simplify types), a problem of practical importance that we did not tackle, yet. [START_REF] Ângelo | Type Inference For Rank-2 Intersection Types Using Set Unification[END_REF] provide a principal type inference for a type system with rank-2 intersection types. In their work, overloaded behaviors are expressible using intersection types, but they are limited by the rank-2 restriction. Union types are not supported, nor are equi-recursive types (actually, it does not feature a general notion of subtyping between two arbitrary types). Their inference does not require backtracking: it generates a set of constraints that are then solved using a set unification algorithm. This approach for inference has some similarities with the one by [START_REF] Castagna | Set-Theoretic Types for Polymorphic Variants[END_REF] improved and further developed by [START_REF] Petrucciani | Polymorphic Set-Theoretic Types for Functional Languages[END_REF] in a context with set-theoretic types, where the set unification algorithm is replaced by tallying in the presence of subtyping. However, while [START_REF] Petrucciani | Polymorphic Set-Theoretic Types for Functional Languages[END_REF]] does support intersection types with no ranking limitation, it is not able to infer intersection types for overloaded functions. Our work aims to improve this aspect, as well as providing a more precise typing of type-cases (occurrence typing).

Work by Oliveira et al. [2016] and [START_REF] Rioux | A Bowtie for a Beast: Overloading, Eta Expansion, and Extensible Data Types in F⊲⊳[END_REF] study disjoint intersection and union types. They allow expressing overloaded behaviors by a general deterministic merge operator. In our work, we do not have a general merge operator: overloaded behaviors only emerge through the use of type-case expressions (or the application of an overloaded function). Our work can be extended with pattern-matching, in which case the first matching branch is selected. This is a different approach than the one used with disjoint intersection types, where branches are disjoint and have no priority and where ambiguous programs are rejected using a notion of mergeability and distinguishability, allowing to define a general merge operator and to support nested composition, which may be useful in some contexts such as compositional programming [START_REF] Zhang | Compositional Programming[END_REF]. [START_REF] Jim | A Polar Type System[END_REF] presents a polar type system which features intersections and parametric polymorphism. In Jim's type system, quantifiers may appear only in positive positions in types, while intersections may only appear in negative positions. This yields a system that is more expressive than rank-2 intersection types, and therefore more expressive than ML. Furthermore, the system features principal types, and a decidable type inference. Some aspects of this work are similar to ours, in particular the use of MGS, an algorithm to compute the most general solution of a (syntactic) sub-typing problem, that plays the same role as our tallying algorithm. Despite these similarities, the approaches differ in the kind of programs they handle: in [START_REF] Jim | A Polar Type System[END_REF]], intersections are only deduced by applying higher-order function parameters to arguments of distinct types within the body of a function, while in our approach, they can also be caused by a type-case.

Finally, set-theoretic types are starting to be integrated into real-world languages, for instance by [START_REF] Schimpf | Set-Theoretic Types for Erlang[END_REF] for Erlang, by [START_REF] Jeffrey | Semantic Subtyping in Luau[END_REF] for Luau, and by Castagna et al. [2023a] for Elixir.

40:29

We believe that, in the future, our work could be used in these systems in order to benefit from a more precise typing of type-cases and pattern matching, as well as by providing an optional type inference that can be used in conjunction with explicit type annotations.

CONCLUSION

This work aims at providing a formal and expressive type system for dynamic languages, where type-cases can be used to give functions an overloaded behavior. It features a type inference that mixes both parametric polymorphism (for modularity) and intersection polymorphism (to capture overloaded behaviors). In that sense, our work is more than a simple study on typability: as a matter of fact, monomorphic intersection and union types are sufficient to type a closed program where all function applications are known (cf., Section 1.2), but this would be bad from a language design point of view, and it is the reason why people program using ML-style programming languages rather than intersection based ones. Separate compilation and modular definitions are requirements of any reasonable programming language. The essence of this work is thus to challenge the limits of how much precision one can obtain (through intersection types)-ideally precise enough to type idioms of dynamic languages-while preserving modularity (thanks to let-polymorphism).

While we believe our work to be an important step towards a better static typing of dynamic languages, several key features are still missing. First, the presence of side effects may invalidate our approach: if the [∨] rule in Figure 2 is applied to two different occurrences of an expression 𝑒 ′ that is not pure, then the rule may type an expression that yields a run-time type error. This can be seen on the algorithmic system, where the transformation into an MSC-form binds the two occurrences of 𝑒 ′ to the same variable, thus wrongly assuming that they both yield the same result. Strictly speaking, our algorithmic approach does not require expressions to be pure; it just needs that when two occurrences of an expression may produce two distinct values that may change the result of a dynamic test, then these two occurrences must be bound by two different binds. Having only pure expressions is a straightforward way to satisfy this property. Having each subexpression bound to a distinct variable (i.e., no sharing, that is, a less precise system, in which the union rule is never used) is a way to retain safety in the presence of side-effects. But between these two extrema, there is a whole palette of less coarse solutions that make it possible to apply our approach in the presence of side-effects, and that we plan to study in future work. This poses two main challenges: (𝑖) how to separate problematic expressions from non-problematic ones (e.g., a gen_id: Unit→Int function performs side-effects, but if its result is tested only against Int, then it is sound to have all occurrences of gen_id bound by the same bind during typing) which, in terms of the type system, corresponds to characterize a class of subexpressions 𝑒 ′ that can be safely used in rule [∨]; and (𝑖𝑖) how to do so before our type inference, at a point when type information is not available, yet.

Second, while the performance of our prototype is reasonable, it can certainly be improved by using more sophisticated implementations techniques and heuristics on the lines we outlined at the end of Section 5.

Third, the interactions between code that is exported and code that is local must be better studied and understood: using intersection for local polymorphic functions and generalization for global ones, may not always be entirely satisfactory since the types of the global functions may be "polluted" by the types of the local applications, yielding less a precise reconstruction for the former. One solution can be to hoist the definition of polymorphic functions at toplevel whenever possible.

Lastly, an important future work is the support of row-polymorphism: while records can be easily added to the present work, the precise typing of functions operating on records requires rowpolymorphism. This is especially important for dynamic languages where records are seamlessly used to encode both objects and dictionaries. A first step in that direction may be to integrate the work by Castagna [2023b], which unifies dictionaries and records.

A EXTENSIONS

In this appendix, we present some extensions for the source language, in particular let-bindings (not to be coufounded with the top-level definitions composing a program: the let-bindings presented in this section can be used anywhere in an expression and do not generalize the type of their definition) and pattern matching.

This section gives an overview of these extensions together with some explanations, but the full semantics and typing rules can be found in the next appendices.

A.1 Let Bindings

A.1.1 Declarative Type System. Let bindings can be added to the syntax of our language:

Expressions 𝑒 ::= • • • | let 𝑥 = 𝑒 in 𝑒
with the following notion of reduction:

let 𝑥 = 𝑣 in 𝑒 𝑒{𝑣/𝑥 }
At first sight, we could think of adding this typing rule to the declarative type system:

[Let] Γ ⊢ 𝑒 1 : 𝑡 1 Γ, 𝑥 : 𝑡 1 ⊢ 𝑒 2 : 𝑡 2 Γ ⊢ let 𝑥 = 𝑒 1 in 𝑒 2 : 𝑡 2
However, this extension of the declarative type system has one issue: let-bindings can introduce aliasing, preventing in some cases the [∨] rule from applying. For instance, consider the following expression:

𝜆𝑥 . let 𝑦 = 𝑥 in (𝑓 𝑥 ∈Int) ? 𝑓 𝑦 : 42 with 𝑓 : 1 → 1. Though for any argument 𝑥 this function yields an integer, it is not possible to derive for it the type 1 → Int using this extension of the declarative type system. Indeed, 𝑓 𝑥 and 𝑓 𝑦 are not syntactically equivalent and thus the [∨] rule can only decompose their types independently, loosing the correlation between these two expressions.

One way to fix this issue is to remove this kind of aliasing before applying the declarative type system. For that, we can introduce an intermediate language featuring an alternative version of let-bindings:

Expressions 𝑒 ::= • • • | let 𝑒 in 𝑒
Let-bindings of the source language can be transformed into this alternative version using a transformation . defined as follows (the other cases are straightforward):

let 𝑥 = 𝑒 1 in 𝑒 2 = let 𝑒 1 in 𝑒 2 { 𝑒 1 /𝑥 }
Finally, the declarative type system can be extended with this rule:

[Let] Γ ⊢ 𝑒 1 : 𝑡 1 Γ ⊢ 𝑒 2 : 𝑡 2 Γ ⊢ let 𝑒 1 in 𝑒 2 : 𝑡 2
A.1.2 Algorithmic Type System. Let-bindings are added to MSC forms as a new atom construction:

Atomic expr 𝑎 ::= • • • | let x in x
The intuition is the same as for the declarative type system: we want to get rid of the aliasing caused by let-bindings, while still using bindings to factorize each subexpression. Indeed, to produce an atom for the expression let 𝑥 = 𝑒 1 in 𝑒 2 we must replace each subexpression by a binding variable, which would yield something of the form let 𝑥 = x 1 in x 2 . Since the body of the let-expression is a variable, then the variable 𝑥 is only an alias for x 1 and thus is undesirable. Consequently, only the other two variables are specified, which yields let x 1 in x 2 and which explains the definition of the atom for let expressions.

For instance, the expression let 𝑥 = 𝜆𝑦.𝑦 in (𝑥, 𝑥) has the following canonical form:

bind x 1 = (𝜆𝑦.bind y = 𝑦 in y ) in bind x 2 = (x 1 , x 1 ) in bind x 3 = (let x 1 in x 2 ) in x 3
Note that, as explained above, the variable 𝑥 is no longer present in the canonical form. The algorithmic type system can then be extended with the following rule:

[Let-Alg] Γ ⊢ A [let x 1 in x 2 | ∅] : Γ(x 2 ) x 1 ∈ dom(Γ)
It is straightforward to extend the reconstruction with additional rules in order to support this new construction (c.f. appendix H).

A.2 Type Constraints

A new construction (𝑒 : 𝜏) can be added to our source language. This construction acts as a type constraint: if the expression 𝑒 does not reduce to a value of type 𝜏 (and does not diverge), then the reduction will be stuck. In a sense, it could be seen as a cast, but we will not use this terminology in order to avoid confusions with gradual typing. Actually, we only introduce this construction because it will be used later to encode more general type-cases.

We add the following construction to our source language:

Expressions 𝑒 ::= • • • | (𝑒 : 𝜏)
with the following notion of reduction:

(𝑣 : 𝜏) 𝑣 if 𝑣 ∈ 𝜏
The declarative type system can trivially be extended by adding this rule:

[Constr] Γ ⊢ 𝑒 : 𝜏 Γ ⊢ 𝑒 : 𝑡 Γ ⊢ (𝑒 : 𝜏) : 𝑡
The same construction is added to the atoms of canonical forms:

Atomic expr 𝑎 ::= • • • | x : 𝜏
The annotations of the algorithmic type system also need to be extended:

Atoms annotations a ::= • • • | :(Σ)
and the algorithmic type system is extended with the following rule:

[Constr-Alg] Γ ⊢ A [x : 𝜏 | :(Σ)] : Γ(x) Γ(x)Σ ≤ 𝜏 40:35
It is also straightforward to extend the reconstruction with additional rules in order to support this new construction (c.f. appendix H).

A.3 Pattern Matching

Pattern matching is a fundamental feature of functional languages, and even some dynamic languages such as Python have started to implement it. In this section, we show how this feature can be added in our source language. We proceed in two steps: first, a more general typecase construct with arbitrary arity is introduced, and secondly, this construct is generalized again so that branches can be decorated with patterns instead of just types.

A.3.1 Extended Typecases. We start by adding a generalized version of the typecase, that can have any number of branches:

Expressions 𝑒 ::= • • • | (tcase 𝑒 of 𝜏 → 𝑒 | . . . | 𝜏 → 𝑒)
with the following notion of reduction:

tcase 𝑣 of 𝜏 1 → 𝑒 1 | . . . | 𝜏 𝑛 → 𝑒 𝑛 𝑒 𝑘 if 𝑣 : 𝜏 𝑘 ∖ ( 𝑖 ∈1. .𝑘-1 𝜏 𝑖 ) for any 𝑘 ∈ 1 . . 𝑛
In terms of typing, however, we choose not to extend the type system with additional rules in order to preserve its minimality. Instead, we transform expressions with extended typecases into expressions of the source langauge presented in section 2, with the let-binding and type constraints extensions (A.1 and A.2). For that, we use the following transformation:

𝑐 = 𝑐 𝑥 = 𝑥 𝜆𝑥 .𝑒 = 𝜆𝑥 . 𝑒 𝜋 𝑖 𝑒 = 𝜋 𝑖 𝑒 𝑒 1 𝑒 2 = 𝑒 1 𝑒 2 (𝑒 1 , 𝑒 2 ) = ( 𝑒 1 , 𝑒 2 ) (𝑒∈𝜏) ? 𝑒 1 : 𝑒 2 = ( 𝑒 ∈𝜏) ? 𝑒 1 : 𝑒 2 let 𝑥 = 𝑒 1 in 𝑒 2 = let 𝑥 = 𝑒 1 in 𝑒 2 𝑒 : 𝜏 = 𝑒 : 𝜏 tcase 𝑒 of 𝜏 1 → 𝑒 1 | . . . | 𝜏 𝑛 → 𝑒 𝑛 = let 𝑥 = ( 𝑒 : 𝑖 ∈1. .𝑛 𝜏 𝑖 ) in c 𝑥 (𝜏 1 → 𝑒 1 ; . . . ; 𝜏 𝑛 → 𝑒 𝑛 ) with 𝑥 fresh c 𝑥 (𝜏 → 𝑒) = 𝑒 c 𝑥 (𝜏 → 𝑒 ; 𝐶) = (𝑥 ∈𝜏) ? 𝑒 : c 𝑥 (𝐶)
A.3.2 Pattern Matching. Now, we introduce patterns and a pattern matching construct in the source language:

Patterns 𝑝 ::= 𝜏 | 𝑥 | 𝑝&𝑝 | 𝑝|𝑝 | (𝑝, 𝑝) | 𝑥 := 𝑐 Expressions 𝑒 ::= • • • | (match 𝑒 with 𝑝 → 𝑒 | . . . | 𝑝 → 𝑒)
The associated reduction rule can be found in Appendix B.

In terms of typing, we proceed as before by transforming an expression with pattern matching into an expression without pattern matching (but with extended typecases and let-bindings), using the following transformation:

𝑐 = 𝑐 𝑥 = 𝑥 𝜆𝑥 .𝑒 = 𝜆𝑥 . 𝑒 𝜋 𝑖 𝑒 = 𝜋 𝑖 𝑒 𝑒 1 𝑒 2 = 𝑒 1 𝑒 2 (𝑒 1 , 𝑒 2 ) = ( 𝑒 1 , 𝑒 2 ) (𝑒∈𝜏) ? 𝑒 1 : 𝑒 2 = ( 𝑒 ∈𝜏) ? 𝑒 1 : 𝑒 2 let 𝑥 = 𝑒 1 in 𝑒 2 = let 𝑥 = 𝑒 1 in 𝑒 2 𝑒 : 𝜏 = 𝑒 : 𝜏 tcase 𝑒 of 𝜏 1 → 𝑒 1 | . . . | 𝜏 𝑛 → 𝑒 𝑛 = tcase 𝑒 of 𝜏 1 → 𝑒 1 | . . . | 𝜏 𝑛 → 𝑒 𝑛 match 𝑒 with 𝑝 1 → 𝑒 1 | . . . | 𝑝 𝑛 → 𝑒 𝑛 = let 𝑥 = 𝑒 in tcase 𝑥 of 𝑝 1 → 𝑒 ′ 1 | . . . | 𝑝 𝑛 → 𝑒 ′ 𝑛 with 𝑥 fresh, 𝜏 = 𝜏 𝑥 = 1 𝑝 1 &𝑝 2 = 𝑝 1 ∧ 𝑝 2 𝑝 1 |𝑝 2 = 𝑝 1 ∨ 𝑝 2 (𝑝 1 , 𝑝 2 ) = 𝑝 1 × 𝑝 2 𝑥 := 𝑐 = 1
and where for every 𝑖 ∈ 1 . . 𝑚:

𝑒 ′ 𝑖 = let 𝑥 1 = d 𝑥 1 (𝑝 𝑖 , 𝑥) in . . . let 𝑥 𝑚 = d 𝑥 𝑚 (𝑝 𝑖 , 𝑥) in 𝑒 𝑖 for {𝑥 1 , ..., 𝑥 𝑚 } = vars(𝑝 𝑖 ) with d 𝑥 (𝑥, 𝑒) = 𝑒 d 𝑥 (𝑥 := 𝑐, 𝑒) = 𝑐 d 𝑥 ((𝑝 1 , 𝑝 2 ), 𝑒) = d 𝑥 (𝑝 𝑖 , 𝜋 𝑖 𝑒) if 𝑥 ∈ vars(𝑝 𝑖 ) d 𝑥 (𝑝 1 &𝑝 2 , 𝑒) = d 𝑥 (𝑝 𝑖 , 𝑒) if 𝑥 ∈ vars(𝑝 𝑖 ) d 𝑥 (𝑝 1 |𝑝 2 , 𝑒) = (𝑒∈ 𝑝 1 ) ? d 𝑥 (𝑝 1 , 𝑒) : d 𝑥 (𝑝 2 , 𝑒)
(𝜆𝑥 .𝑒)𝑣 𝑒{𝑣/𝑥 } 𝜋 1 (𝑣 1 , 𝑣 2 ) 𝑣 1 𝜋 2 (𝑣 1 , 𝑣 2 ) 𝑣 2 (𝑣 ∈𝜏) ? 𝑒 1 : 𝑒 2 𝑒 1 if 𝑣 ∈ 𝜏 (𝑣 ∈𝜏) ? 𝑒 1 : 𝑒 2 𝑒 2 if 𝑣 ∈ ¬𝜏 let 𝑥 = 𝑣 in 𝑒 𝑒{𝑣/𝑥 } (𝑣 : 𝜏) 𝑣 if 𝑣 ∈ 𝜏 tcase 𝑣 of 𝜏 1 → 𝑒 1 | . . . | 𝜏 𝑛 → 𝑒 𝑛 𝑒 𝑘 if 𝑣 : 𝜏 𝑘 ∖ ( 𝑖 ∈1. .𝑘-1 𝜏 𝑖 ) for any 𝑘 ∈ 1 . . 𝑛 match 𝑣 with 𝑝 1 → 𝑒 1 | . . . | 𝑝 𝑛 → 𝑒 𝑛 𝑒 𝑘 (𝑣/𝑝 𝑘 ) if 𝑣 : 𝑝 𝑘 ∖( 𝑖 ∈1. .𝑘-1 𝑝 𝑖 ) for any 𝑘 ∈ 1 . . 𝑛
together with the context rules that implement a leftmost outermost reduction strategy, that is, Capture-avoiding substitutions are defined as follows (cases for extended typecases and patternmatchings have been omitted for concision):

𝐸 [𝑒] 𝐸 [𝑒 ′ ] if 𝑒 𝑒 ′
𝑐{𝑒 ′ /𝑥 } = 𝑐 𝑥 {𝑒 ′ /𝑥 } = 𝑒 ′ 𝑦{𝑒 ′ /𝑥 } = 𝑦 𝑥 ≠ 𝑦 (𝜆𝑥 .𝑒){𝑒 ′ /𝑥 } = 𝜆𝑥 .𝑒 (𝜆𝑦.𝑒){𝑒 ′ /𝑥 } = 𝜆𝑦.(𝑒{𝑒 ′ /𝑥 }) 𝑥 ≠ 𝑦, 𝑦 ∉ fv(𝑒 ′ ) (𝜆𝑦.𝑒){𝑒 ′ /𝑥 } = 𝜆𝑧.(𝑒{𝑧/𝑦}{𝑒 ′ /𝑥 }) 𝑥 ≠ 𝑦, 𝑦 ∈ fv(𝑒 ′ ), 𝑧 fresh (𝑒 1 𝑒 2 ){𝑒 ′ /𝑥 } = (𝑒 1 {𝑒 ′ /𝑥 }) (𝑒 2 {𝑒 ′ /𝑥 }) (𝑒 1 , 𝑒 2 ){𝑒 ′ /𝑥 } = (𝑒 1 {𝑒 ′ /𝑥 }, 𝑒 2 {𝑒 ′ /𝑥 }) (𝜋 𝑖 𝑒){𝑒 ′ /𝑥 } = 𝜋 𝑖 (𝑒{𝑒 ′ /𝑥 }) ((𝑒 1 ∈𝜏) ? 𝑒 2 : 𝑒 3 ){𝑒 ′ /𝑥 } = (𝑒 1 {𝑒 ′ /𝑥 }∈𝜏) ? 𝑒 2 {𝑒 ′ /𝑥 } : 𝑒 3 {𝑒 ′ /𝑥 } (let 𝑥 = 𝑒 1 in 𝑒 2 ){𝑒 ′ /𝑥 } = let 𝑥 = 𝑒 1 {𝑒 ′ /𝑥 } in 𝑒 2 (let 𝑦 = 𝑒 1 in 𝑒 2 ){𝑒 ′ /𝑥 } = let 𝑦 = 𝑒 1 {𝑒 ′ /𝑥 } in 𝑒 2 {𝑒 ′ /𝑥 } 𝑥 ≠ 𝑦, 𝑦 ∉ fv(𝑒 ′ ) (let 𝑦 = 𝑒 1 in 𝑒 2 ){𝑒 ′ /𝑥 } = let 𝑦 = 𝑒 1 {𝑒 ′ /𝑥 } in 𝑒 2 {𝑧/𝑦}{𝑒 ′ /𝑥 } 𝑥 ≠ 𝑦, 𝑦 ∈ fv(𝑒 ′ ), 𝑧 fresh
The relation 𝑣 ∈ 𝜏 that determines whether a value is of a given type or not and holds true if and only if typeof(𝑣) ≤ 𝜏, where

typeof(𝜆𝑥 .𝑒) = 0 → 1 typeof(𝑐) = b 𝑐 typeof((𝑣 1 , 𝑣 2 )) = typeof(𝑣 1 ) × typeof(𝑣 2 )
Finally, the operators used in the reduction rule for pattern matching are defined as follows:

𝜏 = 𝜏 𝑥 = 1 𝑝 1 &𝑝 2 = 𝑝 1 ∧ 𝑝 2 𝑝 1 |𝑝 2 = 𝑝 1 ∨ 𝑝 2 (𝑝 1 , 𝑝 2 ) = 𝑝 1 × 𝑝 2 𝑥 := 𝑐 = 1 and 𝑣/𝜏 = id if 𝑣 : 𝜏 𝑣/𝑥 = {𝑣/𝑥 } 𝑣/(𝑝 1 &𝑝 2 ) = 𝜎 1 ∪ 𝜎 2 if 𝜎 1 = 𝑣/𝑝 1 and 𝜎 2 = 𝑣/𝑝 2 𝑣/(𝑝 1 |𝑝 2 ) = 𝑣/𝑝 1 if 𝑣/𝑝 1 ≠ fail 𝑣/(𝑝 1 |𝑝 2 ) = 𝑣/𝑝 2 if 𝑣/𝑝 1 = fail 𝑣/(𝑝 1 , 𝑝 2 ) = 𝜎 1 ∪ 𝜎 2 if 𝑣 = (𝑣 1 , 𝑣 2 ), 𝜎 1 = 𝑣 1 /𝑝 1 and 𝜎 2 = 𝑣 2 /𝑝 2 𝑣/(𝑥 := 𝑐) = {𝑐/𝑥 } 𝑣/𝑝 = fail otherwise C SUBTYPING RELATION
Subtyping is defined by giving a set-theoretic interpretation of the types of Definition 2.1 into a suitable domain D. In case of polymorphic types, the domain at issue must satisfy the property of convexity [START_REF] Castagna | Set-theoretic Foundation of Parametric Polymorphism and Subtyping[END_REF]. A simple model that satisfies convexity was proposed by [START_REF] Gesbert | A logical approach to deciding semantic subtyping[END_REF]. We succintly present it in this section. The reader may refer to [Castagna 2023a, Section 3.3] for more details.

Definition C.1 (Interpretation domain [START_REF] Gesbert | A logical approach to deciding semantic subtyping[END_REF]). The interpretation domain D is the set of finite terms 𝑑 produced inductively by the following grammar

𝑑 𝑐 𝐿 | (𝑑, 𝑑) 𝐿 | {(𝑑, 𝜕), . . . , (𝑑, 𝜕)} 𝐿 𝜕 𝑑 | Ω
where 𝑐 ranges over the set C of constants, 𝐿 ranges over finite sets of type variables, and where Ω is such that Ω ∉ D.

The elements of D correspond, intuitively, to (denotations of) the results of the evaluation of expressions, labeled by finite sets of type variables. In particular, in a higher-order language, the results of computations can be functions which, in this model, are represented by sets of finite relations of the form {(𝑑 1 , 𝜕 1 ), . . . , (𝑑 𝑛 , 𝜕 𝑛 )} 𝐿 , where Ω (which is not in D) can appear in second components to signify that the function fails (i.e., evaluation is stuck) on the corresponding input. This is implemented by using in the second projection the meta-variable 𝜕 which ranges over D Ω = D ∪ {Ω} (we reserve 𝑑 to range over D, thus excluding Ω). This constant Ω is used to ensure that 1 → 1 is not a supertype of all function types: if we used 𝑑 instead of 𝜕, then every well-typed function could be subsumed to 1 → 1 and, therefore, every application could be given the type 1, independently from its argument as long as this argument is typable (see Section 4.2 of [START_REF] Frisch | Semantic subtyping: dealing set-theoretically with function, union, intersection, and negation types[END_REF]] for details). The restriction to finite relations corresponds to the intuition that the denotational semantics of a function is given by the set of its finite approximations, where finiteness is a restriction necessary (for cardinality reasons) to give the semantics to higher-order functions. Finally, the sets of type variables that label the elements of the domain are used to interpret type variables: we interpret a type variable 𝛼 by the set of all elements that are labeled by 𝛼, that is 𝛼 = {𝑑 | 𝛼 ∈ tags(𝑑)} (where we define tags(𝑐 𝐿 ) = tags((𝑑, 𝑑 ′ ) 𝐿 ) = tags({(𝑑 1 , 𝜕 1 ), . . . , (𝑑 𝑛 , 𝜕 𝑛 )} 𝐿 ) = 𝐿).

We define the interpretation 𝑡 of a type 𝑡 so that it satisfies the following equalities, where P fin denotes the restriction of the powerset to finite subsets and B denotes the function that assigns to each basic type the set of constants of that type, so that for every constant 𝑐 we have 𝑐 ∈ B(b 𝑐 ) (we use b 𝑐 to denote the basic type of the constant 𝑐):

0 = ∅ 𝛼 = {𝑑 | 𝛼 ∈ tags(𝑑)} 𝑡 1 ∨ 𝑡 2 = 𝑡 1 ∪ 𝑡 2 𝑏 = B(𝑏) ¬𝑡 = D ∖ 𝑡 𝑡 1 × 𝑡 2 = 𝑡 1 × 𝑡 2 𝑡 1 →𝑡 2 = {𝑅 ∈ P fin (D×D Ω ) | ∀(𝑑, 𝜕) ∈ 𝑅. 𝑑 ∈ 𝑡 1 =⇒ 𝜕 ∈ 𝑡 2 }
We cannot take the equations above directly as an inductive definition of because types are not defined inductively but coinductively. Notice however that the contractivity condition of Definition 2.1 ensures that the binary relation ▷ ⊆ Types×Types defined by

𝑡 1 ∨ 𝑡 2 ▷ 𝑡 𝑖 , 𝑡 1 ∧ 𝑡 2 ▷ 𝑡 𝑖 , ¬𝑡 ▷ 𝑡 is Noetherian.
This gives an induction principle11 on Types that we use combined with structural induction on D to give the following definition, which validates these equalities.

Definition C.2 (Set-theoretic interpretation of types). We define a binary predicate (𝑑 : 𝑡) ("the element 𝑑 belongs to the type 𝑡"), where 𝑑 ∈ D and 𝑡 ∈ Types, by induction on the pair (𝑑, 𝑡) ordered lexicographically. The predicate is defined as follows:

(𝑐 : 𝑏) = 𝑐 ∈ B(𝑏) (𝑑 : 𝛼) = 𝛼 ∈ tags(𝑑) ((𝑑 1 , 𝑑 2 ) : 𝑡 1 × 𝑡 2 ) = (𝑑 1 : 𝑡 1 ) and (𝑑 2 : 𝑡 2 ) ({(𝑑 1 , 𝜕 1 ), ..., (𝑑 𝑛 , 𝜕 𝑛 )} : 𝑡 1 → 𝑡 2 ) = ∀𝑖 ∈ [1..𝑛]. if (𝑑 𝑖 : 𝑡 1 ) then (𝜕 𝑖 : 𝑡 2 ) (𝑑 : 𝑡 1 ∨ 𝑡 2 ) = (𝑑 : 𝑡 1 ) or (𝑑 : 𝑡 2 ) (𝑑 : ¬𝑡) = not (𝑑 : 𝑡) (𝜕 : 𝑡) = false otherwise
We define the set-theoretic interpretation :

Types → P (D) as 𝑡 = {𝑑 ∈ D | (𝑑 : 𝑡)}.
Finally, we define the subtyping preorder and its associated equivalence relation as follows.

Definition C.3 (Subtyping relation). We define the subtyping relation ≤ and the subtyping equivalence relation ≃ as

𝑡 1 ≤ 𝑡 2 ⇐⇒ def 𝑡 1 ⊆ 𝑡 2 and 𝑡 1 ≃ 𝑡 2 ⇐⇒ def (𝑡 1 ≤ 𝑡 2 ) and (𝑡 2 ≤ 𝑡 1 ) .

D DECLARATIVE TYPE SYSTEM WITH EXTENSIONS

The declarative type system extended with the extensions of Appendix A uses expressions produced by the following grammar:

Expressions 𝑒 ::= 𝑐 | 𝑥 | 𝜆𝑥 .𝑒 | 𝑒𝑒 | (𝑒, 𝑒) | 𝜋 𝑖 𝑒 | (𝑒∈𝜏) ? 𝑒 : 𝑒 | let 𝑒 in 𝑒 | (𝑒 : 𝜏)
Note that extended typecases and pattern matching are absent because they are encoded using let-bindings and type constraints before typing. Similarly, we use the construction let 𝑒 in 𝑒 for let-bindings instead of the initial construction let 𝑥 = 𝑒 in 𝑒 in order to avoid aliasing. You should refer to Section A.1 for more details on this transformation. The deduction rules for the declarative type system are:

Γ ⊢ let 𝑒 1 in 𝑒 2 : 𝑡 2 [Constr] Γ ⊢ 𝑒 : 𝜏 Γ ⊢ 𝑒 : 𝑡 Γ ⊢ (𝑒 : 𝜏) : 𝑡 E COMPUTATION OF MSC-FORMS E.

From Canonical Forms to Source Language Expressions

We recall the grammar for canonical forms, with the extensions presented in Appendix A:

Atomic expressions 𝑎 ::= 𝑐 | 𝑥 | 𝜆𝑥 .𝜅 | (x, x) | xx | 𝜋 𝑖 x | (x∈𝜏) ? x : x | let x in x | x : 𝜏 Canonical Forms 𝜅 ::= x | bind x = 𝑎 in 𝜅
Any canonical form can be transformed into an expression of the source language using the unwiding operator ⌈.⌉ defined as follows:

40:41 ⌈𝑐⌉ = 𝑐 ⌈𝑥⌉ = 𝑥 ⌈𝜆𝑥 .𝜅⌉ = 𝜆𝑥 .⌈𝜅⌉ ⌈x 1 x 2 ⌉ = x 1 x 2 ⌈(x 1 , x 2 )⌉ = (x 1 , x 2 ) ⌈𝜋 𝑖 x⌉ = 𝜋 𝑖 x 𝑖 = 1, 2 ⌈(x∈𝜏) ? x 1 : x 2 ⌉ = (x∈𝜏) ? x 1 : x 2 ⌈let x 1 in x 2 ⌉ = let 𝑥 = x 1 in x 2 {𝑥/x 1 } with 𝑥 fresh ⌈x : 𝜏⌉ = x : 𝜏 ⌈bind x = 𝑎 in 𝜅 ⌉ = ⌈𝜅⌉{⌈𝑎⌉/x} ⌈x⌉ = x

E.2 From Source Language Expressions to Canonical Forms

The inverse direction, that is, producing from a source language expression a canonical form that unwinds to it, is also straightforward.

Let 𝐵 denote a binding context, that is, an ordered list of mappings from binding variables to atoms. Each mapping is written as a pair (x, 𝑒). We note these lists extensionally by separating elements by a semicolon, that is, (x 1 , 𝑎 1 ); . . . ; (x 𝑛 , 𝑎 𝑛 ) and use 𝜀 to denote the empty list.

We define an operation term(𝐵, 𝜅) which takes a binding context 𝐵 and a canonical form 𝜅 and constructs the canonical form containing the bindings listed in 𝐵 and ending with 𝜅, that is:

term(𝜀, 𝜅) = def 𝜅 term(((x, 𝑎); 𝐵), 𝜅) = def bind x = 𝑎 in term(𝐵, 𝜅)
We can now define the function 𝑒 that transforms an expression 𝑒 into a pair (𝐵, x) formed by a binding context 𝐵 and a binding variable x that will be bound to the atom representing 𝑒. The definition is as follows, where x • is a fresh binding variable.

𝑐 = ((x • , 𝑐), x • ) 𝑥 = ((x • , 𝑥), x • ) 𝜆𝑥 .𝑒 = ((x • , 𝜆𝑥 .term 𝑒 ), x • ) 𝜋 𝑖 𝑒 = ((𝐵; (x • , 𝜋 𝑖 x)), x • )
where

(𝐵, x) = 𝑒 𝑒 1 𝑒 2 = ((𝐵 1 ; 𝐵 2 ; (x • , x 1 x 2 )), x • ) where (𝐵 1 , x 1 ) = 𝑒 1 , (𝐵 2 , x 2 ) = 𝑒 2 (𝑒 1 , 𝑒 2 ) = ((𝐵 1 ; 𝐵 2 ; (x • , (x 1 , x 2 ))), x • ) where (𝐵 1 , x 1 ) = 𝑒 1 , (𝐵 2 , x 2 ) = 𝑒 2 (𝑒∈𝜏) ? 𝑒 1 : 𝑒 2 = ((𝐵; 𝐵 1 ; 𝐵 2 ; (x • , (x∈𝜏) ? x 1 : x 2 )), x • )
where

(𝐵, x) = 𝑒 , (𝐵 1 , x 1 ) = 𝑒 1 , (𝐵 2 , x 2 ) = 𝑒 2 x = (𝜀, x) let 𝑥 = 𝑒 1 in 𝑒 2 = ((𝐵 1 ; 𝐵 2 ; (x • , let x 1 in x 2 )), x • )
where

(𝐵 1 , x 1 ) = 𝑒 1 , (𝐵 2 , x 2 ) = 𝑒 2 {x 1 /𝑥 } 𝑒 : 𝜏 = ((𝐵; (x • , x : 𝜏)), x • ) where (𝐵, x) = 𝑒
It is easy to prove that, for any term of the source language 𝑒, ⌈term( 𝑒 )⌉ = 𝑒.

E.3 From Canonical Forms to a MSC Form

It is easy to transform a canonical form into a MSC-form that has the same unwinding. This can be done by applying the rewriting rules below, that are confluent and normalizing.

bind

x 1 = 𝑎 1 in bind x 2 = 𝑎 2 in 𝜅 bind x 1 = 𝑎 1 in 𝜅{x 1 /x 2 } 𝑎 1 ≡ 𝜅 𝑎 2 (3) bind x = 𝑎 in 𝜅 𝜅 x ∉ fv(𝜅) (4) bind x = 𝜆𝑦.( bind z = 𝑎 in 𝜅 • ) in 𝜅 bind z = 𝑎 in bind x = 𝜆𝑦.𝜅 • in 𝜅 𝑦 ∉ fv(𝑎), z ∉ fv(𝜅) (5) 𝜅 1 𝜅 2 ∃𝜅 ′ 1 . 𝜅 1 ≡ 𝜅 𝜅 ′ 1 𝜅 2 (6)
Rule (3) implements the maximal sharing: if two variables bind atoms with the same unwinding (modulo 𝛼-conversion), then the variables are unified. Rule ( 4) removes useless bindings. Rule (5) extrudes bindings from abstractions of variables that do not occur in the argument of the binding. Rule ( 6) applies the previous rule modulo the canonical equivalence: in practice it applies the swap of binding defined in Definition 3.1 as many times as it is needed to apply one of the other rules.

As customary, these rules can be applied under any context. The transformation above transforms every canonical form into an MSC-form that has the same unwinding. It thus allows to compute MSC(𝑒) for any expression 𝑒 of the source language.

F TYPE OPERATORS

The algorithmic type system presented in this work use the following type-operators:

dom(𝑡) = max{𝑢 | 𝑡 ≤ 𝑢 → 1} 𝑡 • 𝑠 = min{𝑢 | 𝑡 ≤ 𝑠 → 𝑢} 𝝅 1 (𝑡) = min{𝑢 | 𝑡 ≤ 𝑢 × 1} 𝝅 2 (𝑡) = min{𝑢 | 𝑡 ≤ 1 × 𝑢}
In words, 𝑡 • 𝑠 is the best (i.e., smallest wrt ≤) type we can deduce for the application of a function of type 𝑡 to an argument of type 𝑠. Projection and domain are standard. All these operators can be effectively computed as shown below (see Castagna et al. [2022a]; [START_REF] Frisch | Semantic subtyping: dealing set-theoretically with function, union, intersection, and negation types[END_REF] for details and proofs).

If any of the types at issue is empty, then the computation is straightforward:

dom(0) = 1 and 0 • 𝑠 = 𝑡 • 0 = 𝝅 1 (0) = 𝝅 2 (0) = 0.
Otherwise the operators are computed as follows.

For 𝑡 ≃ dnf 𝑖 ∈𝐼 𝑝 ′ ∈𝑃 ′ 𝑖 𝛼 𝑝 ′ ∧ 𝑛 ′ ∈𝑁 ′ 𝑖 ¬𝛼 ′ 𝑛 ′ ∧ 𝑝 ∈𝑃 𝑖 (𝑠 𝑝 → 𝑡 𝑝 ) ∧ 𝑛 ∈𝑁 𝑖 ¬(𝑠 ′ 𝑛 → 𝑡 ′ 𝑛 )
, where each summand of the outer union is not empty, the first two operators are computed by:

dom(𝑡) = 𝑖 ∈𝐼 𝑝 ∈𝑃 𝑖 𝑠 𝑝 𝑡 • 𝑠 = 𝑖 ∈𝐼 {𝑄 ⊊𝑃 𝑖 | 𝑠 ≰ 𝑞∈𝑄 𝑠 𝑞 } 𝑝 ∈𝑃 𝑖 ∖𝑄 𝑡 𝑝 (for 𝑠 ≤ dom(𝑡)) 40:43 For 𝑡 ≃ dnf 𝑖 ∈𝐼 𝑝 ′ ∈𝑃 ′ 𝑖 𝛼 𝑝 ′ ∧ 𝑛 ′ ∈𝑁 ′ 𝑖 ¬𝛼 ′ 𝑛 ′ ∧ 𝑝 ∈𝑃 𝑖 (𝑠 𝑝 , 𝑡 𝑝 ) ∧ 𝑛 ∈𝑁 𝑖 ¬(𝑠 ′ 𝑛 , 𝑡 ′ 𝑛 )
, where each summand of the outer union is not empty, the last two operators are computed by

𝝅 1 (𝑡) = 𝑖 ∈𝐼 𝑁 ′ ⊆𝑁 𝑖 𝑝 ∈𝑃 𝑖 𝑠 𝑝 ∧ 𝑛 ∈𝑁 ′ ¬𝑠 ′ 𝑛 𝝅 2 (𝑡) = 𝑖 ∈𝐼 𝑁 ′ ⊆𝑁 𝑖 𝑝 ∈𝑃 𝑖 𝑡 𝑝 ∧ 𝑛 ∈𝑁 ′ ¬𝑡 ′ 𝑛 G ALGORITHMIC TYPE SYSTEM Atom annots a ::= ∅ | 𝜆(u, k) | (𝜌, 𝜌) | @(Σ, Σ) | 𝜋 (Σ) | 0(Σ) | ∈ 1 (Σ) | ∈ 2 (Σ) | ({a, ... , a}) Form annots k ::= 𝜌 | keep (a, {(u, k), . . . , (u, k)}) | skip k | ({k, . . . , k})
The algorithmic type system is defined by the following deduction rules:

[Const-Alg] Γ ⊢ A [𝑐 | ∅] : b 𝑐 [Ax-Alg] Γ ⊢ A [𝑥 | ∅] : Γ(𝑥) [→I-Alg] Γ, 𝑥 : u ⊢ A [𝜅 | k] : 𝑡 Γ ⊢ A [𝜆𝑥 .𝜅 | 𝜆(u, k)] : u → 𝑡 [→E-Alg] Γ ⊢ A [x 1 x 2 | @(Σ 1 , Σ 2 )] : 𝑡 1 • 𝑡 2 𝑡 1 = Γ(x 1 )Σ 1 , 𝑡 2 = Γ(x 2 )Σ 2 𝑡 1 ≤ 0 → 1, 𝑡 2 ≤ dom(𝑡 1 ) [×I-Alg] Γ ⊢ A [(x 1 , x 2 ) | (𝜌 1 , 𝜌 2 )] : 𝑡 1 × 𝑡 2 𝑡 1 = Γ(x 1 )𝜌 1 , 𝑡 2 = Γ(x 2 )𝜌 2 [×E 1 -Alg] Γ ⊢ A [𝜋 1 x | 𝜋 (Σ)] : 𝝅 1 (𝑡) 𝑡 = Γ(x)Σ 𝑡 ≤ (1 × 1) [×E 2 -Alg] Γ ⊢ A [𝜋 2 x | 𝜋 (Σ)] : 𝝅 2 (𝑡) 𝑡 = Γ(x)Σ 𝑡 ≤ (1 × 1) [0-Alg] Γ ⊢ A [(x∈𝜏) ? x 1 : x 2 | 0(Σ)] : 0 Γ(x)Σ ≃ 0 [∈ 1 -Alg] Γ ⊢ A [(x∈𝜏) ? x 1 : x 2 | ∈ 1 (Σ)] : Γ(x 1 ) Γ(x)Σ ≤ 𝜏 [∈ 2 -Alg] Γ ⊢ A [(x∈𝜏) ? x 1 : x 2 | ∈ 2 (Σ)] : Γ(x 2 ) Γ(x)Σ ≤ ¬𝜏 [∧-Alg] (∀𝑖 ∈ 𝐼 ) Γ ⊢ A [𝑎 | a 𝑖 ] : 𝑡 𝑖 Γ ⊢ A [𝑎 | ({a 𝑖 } 𝑖 ∈𝐼 )] : 𝑖 ∈𝐼 𝑡 𝑖 𝐼 ≠ ∅ [Var-Alg] Γ ⊢ A [x | 𝜌] : Γ(x)𝜌 [Bind 1 -Alg] Γ ⊢ A [𝜅 | k] : 𝑡 Γ ⊢ A [bind x = 𝑎 in 𝜅 | skip k] : 𝑡 x ∉ dom(Γ) [Bind 2 -Alg] Γ ⊢ A [𝑎 | a] : 𝑠 (∀𝑖 ∈ 𝐼 ) Γ, x : 𝑠 ∧ u 𝑖 ⊢ A [𝜅 | k 𝑖 ] : 𝑡 𝑖 Γ ⊢ A [bind x = 𝑎 in 𝜅 | keep (a, {(u 𝑖 , k 𝑖 )} 𝑖 ∈𝐼 )] : 𝑖 ∈𝐼 𝑡 𝑖 𝑖 ∈𝐼 u 𝑖 ≃ 1 [∧-Alg] (∀𝑖 ∈ 𝐼 ) Γ ⊢ A [𝜅 | k 𝑖 ] : 𝑡 𝑖 Γ ⊢ A [𝜅 | ({k 𝑖 } 𝑖 ∈𝐼 )] : 𝑖 ∈𝐼 𝑡 𝑖 𝐼 ≠ ∅
To extend the system to type the extensions presented in Appendix A the following rules must be added:

[Let-Alg] Γ ⊢ A [let x 1 in x 2 | ∅] : Γ(x 2 ) x 1 ∈ dom(Γ) [Constr-Alg] Γ ⊢ A [x : 𝜏 | :(Σ)] : Γ(x) Γ(x)Σ ≤ 𝜏 H FULL RECONSTRUCTION SYSTEM

H.1 Main Reconstruction System

Split annotations

S ::= {(u, K), . . . , (u, K)} Atoms intermediate annot.

A ::= infer | untyp | typ | ({A, . . . , A}, {A, . . . , A}) | ∈ 1 | ∈ 2 | 𝜆(u, K) Forms intermediate annot. K ::= infer | untyp | typ | ({K, . . . , K}, {K, . . . , K}) | try-skip (K) | try-keep (A, K, K) | propagate (A, L , S, S) | skip (K) | keep (A, S, S) [Ok] Γ ⊢ R ⟨𝜂 | typ⟩ ⇒ Ok(typ) [Fail] Γ ⊢ R ⟨𝜂 | untyp⟩ ⇒ Fail [Const] Γ ⊢ R ⟨𝑐 | infer⟩ ⇒ Ok(typ) [AxOk] 𝑥 ∈ dom(Γ) Γ ⊢ R ⟨𝑥 | infer⟩ ⇒ Ok(typ) [AxFail] Γ ⊢ R ⟨𝑥 | infer⟩ ⇒ Fail 40:45 [PairVar 𝑖 ] x 𝑖 ∉ dom(Γ) Γ ⊢ R ⟨(x 1 , x 2 ) | infer⟩ ⇒ Var (x 𝑖 , infer, untyp) [PairOk] Γ ⊢ R ⟨(x 1 , x 2 ) | infer⟩ ⇒ Ok(typ) [ProjVar] x ∉ dom(Γ) Γ ⊢ R ⟨𝜋 𝑖 x | infer⟩ ⇒ Var (x, infer, untyp) [ProjInfer] Ψ = tally_infer({Γ(x) ≤ 𝛼 × 𝛽}) Γ ⊢ R ⟨𝜋 𝑖 x | infer⟩ ⇒ Subst(Ψ, typ, untyp) 𝛼, 𝛽 ∈ V 𝑃 fresh [AppVar 𝑖 ] x 𝑖 ∉ dom(Γ) Γ ⊢ R ⟨x 1 x 2 | infer⟩ ⇒ Var (x 𝑖 , infer, untyp) [AppInfer] Ψ = tally_infer({Γ(x 1 ) ≤ Γ(x 2 ) → 𝛼 }) Γ ⊢ R ⟨x 1 x 2 | infer⟩ ⇒ Subst(Ψ, typ, untyp) 𝛼 ∈ V 𝑃 fresh [CaseVar] x ∉ dom(Γ) Γ ⊢ R ⟨(x∈𝜏) ? x 1 : x 2 | infer⟩ ⇒ Var (x, infer, untyp) [CaseSplit] Γ(x) ≰ 𝜏 Γ(x) ≰ ¬𝜏 Γ ⊢ R ⟨(x∈𝜏) ? x 1 : x 2 | infer⟩ ⇒ Split({(x : 𝜏)}, infer, infer) [CaseEmpty] Γ(x) ≃ 0 Γ ⊢ R ⟨(x∈𝜏) ? x 1 : x 2 | infer⟩ ⇒ Ok(typ) [CaseThen] Γ(x) ≤ 𝜏 Ψ = tally_infer({Γ(x) ≤ 0}) Γ ⊢ R ⟨(x∈𝜏) ? x 1 : x 2 | infer⟩ ⇒ Subst(Ψ, typ, ∈ 1 ) [CaseElse] Γ(x) ≤ ¬𝜏 Ψ = tally_infer({Γ(x) ≤ 0}) Γ ⊢ R ⟨(x∈𝜏) ? x 1 : x 2 | infer⟩ ⇒ Subst(Ψ, typ, ∈ 2 ) [CaseVar 𝑖 ] x 𝑖 ∉ dom(Γ) Γ ⊢ R ⟨(x∈𝜏) ? x 1 : x 2 | ∈ 𝑖 ⟩ ⇒ Var (x 𝑖 , typ, untyp) [CaseOk 𝑖 ] Γ ⊢ R ⟨(x∈𝜏) ? x 1 : x 2 | ∈ 𝑖 ⟩ ⇒ Ok(typ) [LambdaInfer] Γ ⊢ R ⟨𝜆𝑥 .𝜅 | 𝜆(𝜶 , infer)⟩ ⇒ R Γ ⊢ R ⟨𝜆𝑥 .𝜅 | infer⟩ ⇒ R 𝜶 ∈ V 𝑀 fresh [LambdaEmpty] Γ ⊢ R ⟨𝜆𝑥 .𝜅 | 𝜆(0, K)⟩ ⇒ Fail [Lambda] Γ, 𝑥 : u ⊢ * R ⟨𝜅 | K⟩ ⇒ R Γ ⊢ R ⟨𝜆𝑥 .𝜅 | 𝜆(u, K)⟩ ⇒ map(𝑋 ↦ → 𝜆(u, 𝑋 ), R)
with map(𝑋 ↦ → 𝑓 (𝑋 ), R) an auxiliary function that applies 𝑓 to each intermediate annotation in R:

map(𝑋 ↦ → 𝑓 (𝑋 ), Ok(H )) = def Ok(𝑓 (H )) map(𝑋 ↦ → 𝑓 (𝑋 ), Fail) = def Fail map(𝑋 ↦ → 𝑓 (𝑋 ), Split(Γ, H 1 , H 2 )) = def Split(Γ, 𝑓 (H 1 ), 𝑓 (H 2 )) Γ ⊢ P ⟨𝑎 | A⟩ ⇒ a Γ ⊢ A [𝑎 | a] : 𝑠 Γ, x : 𝑠 ∧ u ⊢ * R ⟨𝜅 | K⟩ ⇒ Split(Γ ′ , K 1 , K 2 ) x ∈ dom(Γ ′ ) Γ ⊢ E (𝑎 : ¬(u ∧ Γ ′ (x))) ⇒ L 1 Γ ⊢ E (𝑎 : ¬(u ∖ Γ ′ (x))) ⇒ L 2 Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | keep (A, {(u, K)} ∪ S, S ′ )⟩ ⇒ Split(Γ ′ ∖ x, K ′ 1 , K ′ 2 )
where:

• K ′ 1 = propagate (A, L 1 ∪ L 2 , {(u ∧ Γ ′ (x), K 1 ), (u ∖ Γ ′ (x), K 2 )} ∪ S, S ′ ) • K ′ 2 = keep (A, {(u, K 2 )} ∪ S, S ′ ) [BindKeep 3 ] Γ ⊢ P ⟨𝑎 | A⟩ ⇒ a Γ ⊢ A [𝑎 | a] : 𝑠 Γ, x : 𝑠 ∧ u ⊢ * R ⟨𝜅 | K⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | keep (A, {(u, K)} ∪ S, S ′ )⟩ ⇒ R ′ where R ′ = map(𝑋 ↦ → keep (A, {(u, 𝑋 )} ∪ S, S ′ ), R). [BindProp 1 ] Γ ′ ∈ L compatible(Γ, Γ ′ ) Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | propagate (A, L , S, S ′ )⟩ ⇒ Split(Γ ′′ , K 1 , K 2 )
where:

• compatible(Γ, Γ ′ ) ⇔ (dom(Γ ′ ) ⊆ dom(Γ)) and (∀x ∈ dom(Γ ′ ). (Γ(x) ∧ Γ ′ (x) 0) or (Γ(x) ≃ 0)) • Γ ′′ = {(x : u) ∈ Γ ′ | Γ(x) ≰ u} • K 1 = keep (A, S, S ′ ) • K 2 = propagate (A, L ∖ {Γ ′ }, S, S ′ ) [BindProp 2 ] Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | keep (A, S, S ′ )⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | propagate (A, L , S, S ′ )⟩ ⇒ R [InterEmpty] Γ ⊢ R ⟨𝜂 | (∅, ∅)⟩ ⇒ Fail [InterOk] Γ ⊢ R ⟨𝜂 | (∅, 𝑆)⟩ ⇒ Ok( (∅, 𝑆)) [Inter 1 ] Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ Ok(H ′ ) Γ ⊢ R ⟨𝜂 | (𝑆, {H ′ } ∪ 𝑆 ′ )⟩ ⇒ R Γ ⊢ R ⟨𝜂 | ({H } ∪ 𝑆, 𝑆 ′ )⟩ ⇒ R [Inter 2 ] Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ Fail Γ ⊢ R ⟨𝜂 | (𝑆, 𝑆 ′ )⟩ ⇒ R Γ ⊢ R ⟨𝜂 | ({H } ∪ 𝑆, 𝑆 ′ )⟩ ⇒ R [Inter 3 ] Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ R Γ ⊢ R ⟨𝜂 | ({H } ∪ 𝑆, 𝑆 ′ )⟩ ⇒ map(𝑋 ↦ → ( ({𝑋 } ∪ 𝑆, 𝑆 ′ )), R) [Iterate 1 ] Γ ⊢ R ⟨𝜂 | H ⟩ ⇒ Split(Γ ′ , H 1 , H 2 ) Γ ⊢ * R ⟨𝜂 | H 1 ⟩ ⇒ R ′ Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ R ′ Γ ′ = ∅ [Iterate 2 ] Γ ⊢ R ⟨𝜂 | H ⟩ ⇒ Subst({𝜓 𝑖 } 𝑖 ∈𝐼 , H 1 , H 2 ) Γ ⊢ * R ⟨𝜂 | ({H 1 𝜓 𝑖 } 𝑖 ∈𝐼 ∪ {H 2 }, ∅)⟩ ⇒ R ′ Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ R ′ ∀𝑖 ∈ 𝐼 . 𝜓 𝑖 #Γ [Stop] Γ ⊢ R ⟨𝜂 | H ⟩ ⇒ R Γ ⊢ * R ⟨𝜂 | H ⟩ ⇒ R
with H𝜓 denoting the intermediate annotation H in which the substitution 𝜓 has been applied recursively to every type (in 𝜆-abstraction annotations and binding annotations).

The following rules can be added to support the extensions presented in Appendix A: , typ, untyp) 40:49

[LetVar 𝑖 ] x 𝑖 ∉ dom(Γ) Γ ⊢ R ⟨let x 1 in x 2 | infer⟩ ⇒ Var (x 𝑖 , infer, untyp) [LetOk] Γ ⊢ R ⟨let x 1 in x 2 | infer⟩ ⇒ Ok(typ) [ConstrVar] x ∉ dom(Γ) Γ ⊢ R ⟨x : 𝜏 | infer⟩ ⇒ Var (x, infer, untyp) [ConstrInfer] Ψ = tally_infer({Γ(x) ≤ 𝜏 }) Γ ⊢ R ⟨x : 𝜏 | infer⟩ ⇒ Subst(Ψ

H.2 Auxiliary Reconstruction System

In the following, refresh(𝑡) denotes a renaming from vars(𝑡) ∩ V 𝑃 to fresh polymorphic variables.

[Const]

Γ ⊢ P ⟨𝑐 | typ⟩ ⇒ ∅ [Ax] Γ ⊢ P ⟨𝑥 | typ⟩ ⇒ ∅ 𝑥 ∈ dom(Γ) [Pair] 𝜌 1 = refresh(Γ(x 1 )) 𝜌 2 = refresh(Γ(x 2 )) Γ ⊢ P ⟨(x 1 , x 2 ) | typ⟩ ⇒ (𝜌 1 , 𝜌 2 ) [Proj] Σ = tally({Γ(x) ≤ 𝛼 × 𝛽}) Γ ⊢ P ⟨𝜋 𝑖 x | typ⟩ ⇒ 𝜋 (Σ) Σ ≠ ∅ 𝛼, 𝛽 ∈ V 𝑃 fresh [App] 𝑡 1 = Γ(x 1 ) 𝑡 2 = Γ(x 2 ) 𝜌 1 = refresh(𝑡 1 ) 𝜌 2 = refresh(𝑡 2 ) Σ = tally({𝑡 1 𝜌 1 ≤ 𝑡 2 𝜌 2 → 𝛼 }) Γ ⊢ P ⟨x 1 x 2 | typ⟩ ⇒ @({𝜎 • 𝜌 1 | 𝜎 ∈ Σ}, {𝜎 • 𝜌 2 | 𝜎 ∈ Σ}) Σ ≠ ∅ 𝛼 ∈ V 𝑃 fresh [Case 0 ] 𝜎 ∈ tally({Γ(x) ≤ 0}) Γ ⊢ P ⟨(x∈𝜏) ? x 1 : x 2 | typ⟩ ⇒ 0({𝜎 }) [Case 1 ] 𝜎 ∈ tally({Γ(x) ≤ 𝜏 }) Γ ⊢ P ⟨(x∈𝜏) ? x 1 : x 2 | typ⟩ ⇒ ∈ 1 ({𝜎 }) Γ ⊢ P ⟨𝑎 | A⟩ ⇒ a Γ ⊢ A [𝑎 | a] : 𝑠 (∀𝑖 ∈ 𝐼 ) Γ, x : 𝑠 ∧ u 𝑖 ⊢ P ⟨𝜅 | K 𝑖 ⟩ ⇒ k 𝑖 Γ ⊢ P ⟨bind x = 𝑎 in 𝜅 | keep (A, ∅, {(u 𝑖 , K 𝑖 )} 𝑖 ∈𝐼 )⟩ ⇒ keep (a, {(u 𝑖 , k 𝑖 )} 𝑖 ∈𝐼 ) ( * )
where ( * ) is 𝑖 ∈𝐼 u 𝑖 ≃ 1.

[Inter]

(∀𝑖 ∈ 𝐼 ) Γ ⊢ P ⟨𝜂 | H 𝑖 ⟩ ⇒ h 𝑖 Γ ⊢ P ⟨𝜂 | (∅, {H 𝑖 } 𝑖 ∈𝐼 )⟩ ⇒ ({h 𝑖 } 𝑖 ∈𝐼 ) 𝐼 ≠ ∅
The following rules can be added to support the extensions presented in Appendix A:

[Let]

Γ ⊢ P ⟨let x 1 in x 2 | typ⟩ ⇒ ∅ [Constr]
𝜎 ∈ tally({Γ(x) ≤ 𝜏 }) Γ ⊢ P ⟨x : 𝜏 | typ⟩ ⇒ :({𝜎 })

H.3 Split Propagation System

The split propagation system defined in this section tries to deal with the following problem: given an environment Γ, an atom 𝑎 and a type 𝑡, what additional assumptions can be made on Γ in order to ensure that 𝑎 has type 𝑡? It is used by the main reconstruction system in order to propagate splits made by bindings.

[Const 1 ] b 𝑐 ≤ u Γ ⊢ E (𝑐 : u) ⇒ {∅} [Const 2 ] Γ ⊢ E (𝑐 : u) ⇒ {} [Ax 1 ] Γ(𝑥) ≤ u Γ ⊢ E (𝑥 : u) ⇒ {∅} [Ax 2 ] Γ ⊢ E (𝑥 : u) ⇒ {} [Proj 1 ] Γ ⊢ E (𝜋 1 x : u) ⇒ {{x : u × 1}} [Proj 2 ] Γ ⊢ E (𝜋 2 x : u) ⇒ {{x : 1 × u}} [Pair] u ≃ dnf ( 𝑖 ∈𝐼 (u 𝑖 × v 𝑖 )) ∨ . . . Γ ⊢ E ((x 1 , x 2 ) : u) ⇒ {{x 1 : u 𝑖 } ∧ {x 2 : v 𝑖 } | 𝑖 ∈ 𝐼 } [Case]
Γ ⊢ E ((x∈𝜏) ? x 1 : x 2 : u) ⇒ {{x : 𝜏, x 1 : u}, {x : ¬𝜏, x 2 : u}}

[App] Γ(x 1 ) ≃ dnf 𝑖 ∈𝐼 𝑡 𝑖 ∀𝑖 ∈ 𝐼 . {𝜎 𝑗 } 𝑗 ∈𝐽 𝑖 = tally({𝑡 𝑖 ≤ 𝛼 → u}) Γ ⊢ E (x 1 x 2 : u) ⇒ 𝑖 ∈𝐼 L 𝑖 𝛼 ∈ V 𝑃 fresh
where, for every 𝑖 ∈ 𝐼 , L 𝑖 = {{x 1 : (𝑡 𝑖 𝜎 𝑗 )𝜎 ′ 𝑗 , x 2 : (𝛼𝜎 𝑗 )𝜎 ′ 𝑗 } | 𝑗 ∈ 𝐽 𝑖 } with 𝜎 ′ 𝑗 a type substitution mapping each polymorphic type variable 𝛽 appearing in 𝑡 𝑖 𝜎 𝑗 or 𝛼𝜎 𝑗 to either:

• 1 if 𝛽 only appears in covariant positions in 𝛼𝜎 𝑗 ,

• 0 if 𝛽 only appears in contravariant positions in 𝛼𝜎 𝑗 ,

• a fresh monomorphic type variable otherwise.

[Lambda]

Γ ⊢ E (𝜆𝑥 . 𝜅 : u) ⇒ {}

The following rules can be added to support the extensions presented in Appendix A:

[Let]

Γ ⊢ E (let

x 1 in x 2 : u) ⇒ {{x 2 : u}} [Constr]
Γ ⊢ E (x : 𝜏 : u) ⇒ {{x : u}}

I PROOFS

The proofs are for the source language presented in section 2 without extension:

Expressions 𝑒 ::= 𝑐 | 𝑥 | 𝜆𝑥 .𝑒 | 𝑒𝑒 | (𝑒, 𝑒) | 𝜋 𝑖 𝑒 | (𝑒∈𝜏) ? 𝑒 : 𝑒
We fix some notations relative to substitutions:

• 𝜙 ranges over substitutions from type variables (V 𝑃 ∪V 𝑀 ) to types • 𝜌 ranges over renamings of polymorphic variables, that is, injective substitutions from V 𝑃 to V 𝑃 • 𝜎 ranges over substitutions from polymorphic type variables V 𝑃 to types • Σ ranges over sets of substitutions from polymorphic type variables V 𝑃 to types • 𝜓 ranges over substitutions from monomorphic type variables V 𝑀 to monomorphic types 40:51

• Ψ ranges over sets of substitutions from monomorphic type variables V 𝑀 to monomorphic types I.1 A Canonical Form for the Derivations Derivations for the declarative type system can have many shapes (see Appendix D for the full declarative system, without the rules for extensions). In particular, the union elimination rule [∨E] can be used anywhere in the derivation and changes the expression to type by performing a substitution on it. Other non-structural rules such as [∧], [≤] and [Inst] can also be applied anywhere in the derivation. In this section, we will define canonical derivations that restrict the use of those rules. This will then be used, in Section I.2, to establish a type safety theorem.

I.1.1 Alternative Form of the Declarative Type System. In order to be able to express our normalisations lemmas, we first need to slightly modify some rules of the declarative type system. In order to avoid confusions, the modified declarative type system will use this turnstile symbol: ⊢ : . First, we modify the [Ax] rule so that it can perform a renaming of the polymorphic type variables in Γ(𝑥):

[Ax] Γ ⊢ : 𝑥 : Γ(𝑥)𝜌
This new [Ax] rule is derivable in the initial delcarative type system by composing a [Ax] rule and a [Inst] rule. Still, allowing the [Ax] rule to perform a renaming of polymorphic type variables is useful, as it allows to uncorrelate types without resorting to the [Inst] rule. For instance, consider the pair (𝑥, 𝑥) with 𝑥 having the type 𝛼 → 𝛼. While this pair could be typed (𝛼 → 𝛼)×(𝛼 → 𝛼), this type does not allow instantiating the left-hand side and right-hand side of the product independently. A better type would be (𝛼 → 𝛼) × (𝛽 → 𝛽), and with this new [Ax] rule, it can be derived without having to use a [Inst] rule. This way, the [Inst] rule can be reserved to cases that require non trivial instantiations (i.e. not just renamings). Note that the necessity of performing this renaming comes from the fact that we do not use type schemes ∀ ì 𝛼 . 𝑡, where renaming of the type variables in ì 𝛼 can be performed implicitely anywhere. Secondly, we use a [∧] rule of multiple arity instead of a binary one:

[∧]

(∀𝑖 ∈ 𝐼 ) Γ ⊢ : 𝑒 : 𝑡 𝑖 Γ ⊢ : 𝑒 : 𝑖 ∈𝐼 𝑡 𝑖 𝐼 ≠ ∅
This allows to combine successive [∧] rule applications into one [∧] rule, making the normalisation lemmas easier to express. This new [∧] rule is admissible in the ⊢ system: it can be replaced by several consecutive [∧] nodes.

Similarly, we will use a [∨] rule of multiple arity:

[∨] Γ ⊢ : 𝑒 ′ : 𝑠 (∀𝑖 ∈ 𝐼 ) Γ, 𝑥 : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 Γ ⊢ : 𝑒{𝑒 ′ /𝑥 } : 𝑡 {u 𝑖 } 𝑖 ∈𝐼 ∈ Part(1)
with Part(𝑡) denoting the set of partitions of the type 𝑡, that is, the set of all sets {𝑡 𝑖 } 𝑖 ∈𝐼 such that: (𝑖) When needed, we will use the notation 𝑥 /x to range over both binding variables and lambda variables. For instance, we could write the proposition ∀ 𝑥 /x ∈ dom(Γ). Γ( 𝑥 /x) 0. We say that an expression 𝑒 is a ground expression if 𝑒 does not contain any binding variable, and that a derivation 𝐷 is a ground derivation if it derives a judgement for a ground expression. For what concerns programs, they use lambda variables for top-level definitions, and are only composed of ground expressions.

This new system is equivalent to the initial type system: the combination of both [Ax 𝜆 ] and [Ax ∨ ] gives the previous [Ax] rule.

A full declarative type system with these modifications is presented in Figure 4.

The rules

[Const], [Ax 𝜆 ], [→I], [→E], [×I], [×E 1 ], [×E 2 ], [0], [∈ 1
] and [∈ 2 ] will be called structural rules as their use is guided by the structure of the expression to type, each of them allowing to type a specific syntactic construction. In particular, note that the rule [Ax ∨ ] is not considered structural as binding variables x are not supposed to appear in the initial expression (they are only introduced in the derivation when using a [∨] rule).

Also, the first premise of a [∨] rule will be called its definition premise, and its others premises will be called body premises.

All the proofs in the next sections and chapters will use the ⊢ : declarative type system, which is equivalent to the ⊢ type system. Proof. Let 𝐶 a derivation for Γ ⊢ : 𝑒 x : 1. We collect in 𝐷 the set {𝐶 𝑘 } 𝑘 ∈𝐾 of all the subderivations for the expression 𝑒 x . As substitutions are capture-avoiding, no variable in fv(𝑒 x ) could have been introduced in the environment by a [→I] or [∨] node in 𝐷. Thus, we know that the derivations {𝐶 𝑘 } 𝑘 ∈𝐾 are still valid under the initial environment Γ.

Thus, we can build the following derivation:

[∨] [∧] 𝐶 Γ ⊢ : 𝑒 x : 1 𝐶 𝑘 Γ ⊢ : 𝑒 x : 𝑡 𝑘 ∀𝑘 ∈ 𝐾 Γ ⊢ : 𝑒 x : 𝑘 ∈𝐾 𝑡 𝑘 𝐷 ′ 𝑖 Γ, x : ( 𝑘 ∈𝐾 𝑡 𝑘 ) ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ∀𝑖 ∈ 𝐼 Γ ⊢ : 𝑒{𝑒 x /x} : 𝑡
with each 𝐷 ′ 𝑖 being a derivation easily derived from 𝐷 by substituting 𝑒 x by x when relevant, using a [Ax ∨ ] rule on x instead of a subderivation for 𝑒 x when necessary, and by using monotonicity (Lemma I.4). The hypothesis on the derivation 𝐷 ensures that it does not contain any conflicting [∨] node that would become inapplicable due to the fact that 𝑒 x has been substituted by x. □ Lemma I.6 (Elimination of aliasing). Let 𝐷 be a ground derivation, and 𝑁 be a [∨] node in 𝐷 applying a substitution {x/y}. Then, 𝑁 can be removed from 𝐷, without adding any new [∨] node nor structural node in 𝐷.

Proof. The following transformation can be performed to the subderivation introducing x (as 𝐷 is a ground derivation, there must be a [∨] node that introduces x):

40:55 [∨] 𝐴 Γ ⊢ : 𝑒 x : 𝑠 𝐵 Γ, x : 𝑠 ∧ u 𝑘 ⊢ : 𝑒 : 𝑡 𝐸 𝑖 Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ∀𝑖 ∈ 𝐼 ∖ {𝑘 } Γ ⊢ : 𝑒{𝑒 x /x} : 𝑡
where 𝐵 is a derivation that contains this subderivation 𝑆 (whose root is the node 𝑁 to eliminate):

[∨] 𝐶 Γ ′ ⊢ : x : 𝑠 ′ 𝐷 𝑗 Γ ′ , y : 𝑠 ′ ∧ u ′ 𝑗 ⊢ : 𝑒 ′ : 𝑡 ′ ∀𝑗 ∈ 𝐽 Γ ′ ⊢ : 𝑒 ′ {x/y} : 𝑡 ′ with Γ ′ = (Γ, x : 𝑠 ∧ u 𝑘 ) ∪Γ ′′ for some Γ ′′ ↓ (transformed into) [∨] 𝐴 Γ ⊢ : 𝑒 x : 𝑠 𝐵 ′ 𝑗 Γ, x : 𝑠 ∧ u 𝑘 ∧ u ′ 𝑗 ⊢ : 𝑒 : 𝑡 ∀𝑗 ∈ 𝐽 𝐸 𝑖 Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ∀𝑖 ∈ 𝐼 ∖ {𝑘 } Γ ⊢ : 𝑒{𝑒 x /
∧ u 𝑘 ∧ u ′ 𝑗 ≤ 𝑠 ′ ∧ u ′ 𝑗 . □
An interesting thing to note is that the proof above would not work in the presence of a generalization rule such as the one used at top-level:

[Gen] Γ ⊢ : 𝑒 : 𝑡 Γ ⊢ : 𝑒 : 𝑡𝜙 𝜙#Γ
Indeed, the guard condition 𝜙#Γ may prevent monotonicity. More precisely, in the transformation above, if the partition {u 𝑖 } 𝑖 ∈𝐼 introduces a new monomorphic type variable, its introduction earlier in the environment might prevent a potential application of a [Gen] rule in the derivation 𝐵. This impossibility to eliminate aliasing would be an issue for the normalisation lemma (Lemma I.13) detailled below, as it states, in particular, that the union elimination rule only needs to be applied once for a given sub-expression. This is the reason why, in our declarative type system, generalisation only occurs at top-level. Definition I.7 (Acceptable [∨] node). In any derivation, a [∨] node 𝑁 doing the substitution 𝑒{𝑒 ′ /x} is said acceptable if it satisfies the following constraints:

• 𝑒 contains x (no useless definition), and Proof. We proceed by induction on (𝑛 ∨ , 𝑛) (using the lexicographic order), where 𝑛 ∨ denotes the number of [∨] nodes in the derivation, and 𝑛 denotes the total number of nodes in the derivation.

𝜎 ∈Σ ( 𝑖 ∈1. .𝑛 𝑡 ′ 𝑖 )𝜎 ≃ (𝜎 1 ,...,𝜎 𝑛 ) ∈Σ 1 ו••×Σ 𝑛 ( 𝑖 ∈1. .𝑛 𝑡 ′ 𝑖 ) (𝜎 1 ∪ . . . ∪𝜎 𝑛 ) ≃ (𝜎 1 ,...,𝜎 𝑛 ) ∈Σ 1 ו••×Σ 𝑛 𝑖 ∈1. .𝑛 𝑡 ′ 𝑖 (𝜎 1 ∪ . . . ∪𝜎 𝑛 ) ≃ (𝜎 1 ,...,𝜎 𝑛 ) ∈Σ 1 ו••×Σ 𝑛 𝑖 ∈1. .𝑛 𝑡 ′ 𝑖 𝜎 𝑖 ≃ 𝑖 ∈1. .𝑛
If the root is a [Inst] or [≤], we can remove the root (its premise will be the new root) and proceed inductively on the result.

If the root is a [∧], we proceed inductively on all its premises and update the intersection type 𝑡 derived by the root into a new intersection type 𝑡 ′ (according to the new premises). We know that the new derived type 𝑡 ′ satisfies 𝑡 ′ ≤ P 𝑡 according to Proposition I.15.

If the root is a [∨] of the following form: (1) We first proceed inductively on 𝐴, which gives a derivation 𝐴 ′ . We consider following derivation, where 𝑠 ′ ≤ P 𝑠 (and thus 𝑠 ′ ∧ u 𝑖 ≤ P 𝑠 ∧ u 𝑖 ):

[∨] 𝐴 ′ Γ ⊢ : 𝑒 ′ : 𝑠 ′ 𝐵 ′ 𝑖 Γ, x : 𝑠 ′ ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ∀𝑖 ∈ 𝐼 Γ ⊢ : 𝑒{𝑒 ′ /x} : 𝑡
with 𝐵 ′ 𝑖 a derivation easily derived from 𝐵 𝑖 by monotonicity (Lemma I.4). Note that the application of the monotonicity lemma might insert unwanted [Inst∧≤] patterns after axioms, but they will be eliminated with the next step.

(2) The next step is to proceed inductively on the {𝐵 ′ 𝑖 } 𝑖 ∈𝐼 premises, yielding some derivations {𝐵 ′′ 𝑖 } 𝑖 ∈𝐼 that derive some types {𝑡 𝑖 } 𝑖 ∈𝐼 (with ∀𝑖 ∈ 𝐼 . 𝑡 𝑖 ≤ P 𝑡). We can suppose that all the {𝑡 𝑖 } 𝑖 ∈𝐼 have disjoint polymorphic type variables: if it is not the case, it can be ensured by applying Lemma I.14 to these premises. Then, we consider the following derivation:

[∨] 𝐴 ′ Γ ⊢ : 𝑒 ′ : 𝑠 ′ [≤] 𝐵 ′′ 𝑖 Γ, x : 𝑠 ′ ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 𝑖 Γ, x : 𝑠 ′ ∧ u 𝑖 ⊢ : 𝑒 : 𝑖 ∈𝐼 𝑡 𝑖 ∀𝑖 ∈ 𝐼 Γ ⊢ : 𝑒{𝑒 ′ /x} : 𝑖 ∈𝐼 𝑡 𝑖
The result 𝑖 ∈𝐼 𝑡 𝑖 satisfies 𝑖 ∈𝐼 𝑡 𝑖 ≤ P 𝑡 according to Proposition I.16. (3) The new [≤] nodes that appear as premise of the [∨] root could break the properties of Lemma I.13 if the corresponding 𝐵 ′′ 𝑖 ends with a [∨] node. In this case, we move up the faulty [≤] nodes as needed using this transformation: Proof. We proceed by induction on (𝑛 ∨ , 𝑛) (using the lexicographic order), with 𝑛 ∨ the number of [∨] nodes in the derivation, and 𝑛 the total number of nodes in the derivation.

[≤] [∨] 𝐴 Γ ⊢ : 𝑒 ′ : 𝑠 𝐵 𝑖 Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ′ ∀𝑖 ∈ 𝐼 Γ ⊢ : 𝑒{𝑒 ′ /x} : 𝑡 ′ Γ ⊢ : 𝑒{𝑒 ′ /x} : 𝑡 ↓ [∨] 𝐴 Γ ⊢ : 𝑒 ′ : 𝑠 [≤] 𝐵 𝑖 Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ′ Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ∀𝑖 ∈ 𝐼 Γ ⊢ : 𝑒{𝑒 ′ /
If the root is a [≤] or [Inst], we can remove the root (its premise will be the new root) and proceed by induction on its premise.

If the root is a [∧], we proceed inductively on all its premises and update the intersection type 𝑡 derived by the root into a new intersection type 𝑡 ′ (according to the new premises). We trivially have 𝑡 ′ ≤ P 𝑡.

If the root is a [→I] pattern, we proceed inductively on its premise and update the arrow type 𝑡 derived by the root into a new arrow type 𝑡 ′ (according to the new premise). We trivially have 𝑡 ′ ≤ P 𝑡.

If the root is a [∨] of the following form:

[∨] 𝐴 Γ ⊢ : 𝑒 ′ : 𝑠 𝐵 𝑖 Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ∀𝑖 ∈ 𝐼 Γ ⊢ : 𝑒{𝑒 ′ /x} : 𝑡 (1)
We first proceed inductively on 𝐴, yielding a derivation 𝐴 ′ . We then consider the following derivation, with 𝑠 ′ ≤ P 𝑠:

[∨] 𝐴 ′ Γ ⊢ : 𝑒 ′ : 𝑠 ′ 𝐵 ′ 𝑖 Γ, x : 𝑠 ′ ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ∀𝑖 ∈ 𝐼 Γ ⊢ : 𝑒{𝑒 ′ /x} : 𝑡
where each 𝐵 ′ 𝑖 is derived from 𝐵 𝑖 by applying Lemma I.4 (monotonicity), and then Lemma I.18 in order to normalize [Inst] nodes that might have been introduced by the monotonicity lemma. Note that this might add unwanted [≤] nodes, but they will be eliminated with the next step.

(2) We proceed inductively on the {𝐵 ′ 𝑖 } 𝑖 ∈𝐼 premises, yielding some derivations {𝐵 ′′ 𝑖 } 𝑖 ∈𝐼 that derive some types {𝑡 𝑖 } 𝑖 ∈𝐼 (with ∀𝑖 ∈ 𝐼 . 𝑡 𝑖 ≤ P 𝑡). Then, we consider the following derivation:

[∨] 𝐴 ′ Γ ⊢ : 𝑒 ′ : 𝑠 ′ [≤] 𝐵 ′′ 𝑖 Γ, x : 𝑠 ′ ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 𝑖 Γ, x : 𝑠 ′ ∧ u 𝑖 ⊢ : 𝑒 : 𝑖 ∈𝐼 𝑡 𝑖 ∀𝑖 ∈ 𝐼 Γ ⊢ : 𝑒{𝑒 ′ /x} : 𝑖 ∈𝐼 𝑡 𝑖
The result 𝑖 ∈𝐼 𝑡 𝑖 trivially satisfies 𝑖 ∈𝐼 𝑡 𝑖 ≤ P 𝑡.

(3) The new [≤] nodes that appear as premise of the [∨] root could break the properties of Lemma I.13 if the corresponding 𝐵 ′′ 𝑖 ends with a [∨] node. In this case, we move up the faulty [≤] nodes as needed using this transformation:

[≤] [∨] 𝐴 Γ ⊢ : 𝑒 ′ : 𝑠 𝐵 𝑖 Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ′ ∀𝑖 ∈ 𝐼 Γ ⊢ : 𝑒{𝑒 ′ /x} : 𝑡 ′ Γ ⊢ : 𝑒{𝑒 ′ /x} : 𝑡 ↓ [∨] 𝐴 Γ ⊢ : 𝑒 ′ : 𝑠 [≤] 𝐵 𝑖 Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ′ Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ∀𝑖 ∈ 𝐼 Γ ⊢ : 𝑒{𝑒 ′ /x} : 𝑡
The other cases are straightforward. □ Definition I.21 (Canonical derivation). Given an expression order ≤, a canonical derivation for the expression order ≤ is a [∨][Inst][≤]-canonical derivation. We say it is a canonical form derivation if it is also a form derivation, and a canonical atomic derivation if it is an atomic derivation.

When qualifying a derivation 𝐷 of canonical, the order ≤ may be omitted: in this case, we consider that there exists an expression order ≤ such that 𝐷 is canonical for ≤.

Theorem I.22 (Normalisation of derivations). Given an expression order ≤, any ground derivation 𝐷 of Γ ⊢ : 𝑒 : 𝑡 can be transformed into a canonical form derivation of Γ ⊢ : 𝑒 : 𝑡 ′ , for the order ≤ and with 𝑡 ′ ≤ P 𝑡.

Proof. By successive application of Lemma I.13, Lemma I.18, and Lemma I.20. □

I.2 Type Safety

I.2.1 The Parallel Semantics. One technical difficuulty is that the subject reduction does not hold for the semantics presented in Figure 1: performing a reduction step on an expression 𝑒 might break the use of a [∨] rule. Indeed, if in the original typing derivation a rule [∨] substitutes multiple occurrences of the sub-expression 𝑒 by a variable 𝑥, reducing one occurrence of 𝑒 but not the others can make the application of this [∨] rule impossible: the correlation between the reduced 𝑒 and the other occurrences of 𝑒 is thus lost.

To circumvent this issue, we introduce a notion of parallel reduction which forces to reduce all occurrences of a sub-expression at the same time. The idea is to first define reduction rules that only apply at top-level, and then define a context rule (rule [𝜅] below) that allows reducing under an evaluation context, but that will apply this reduction everywhere in the term. With this alternative semantics, the subject reduction becomes true, allowing to prove type safety. The type safety for the initial semantics (Figure 1) is then deduced from this result.

The parallel semantics is formalized in Figure 5. A step of reduction happening at top-level is noted ⊤ , and a step of reduction of the parallel semantics under any evaluation context is noted P . Notice that the rule [𝜅] applies on an expression 𝑒 ′′ a substitution from an expression 𝑒 ′ to an expression 𝑒, noted 𝑒 ′′ {𝑒/𝑒 ′ }, which is defined inductively on 𝑒 ′′ as follows: As defined in Section I.1, we consider that expressions of the source language can contain binding variables. In particular, the unwinding operator ⌈.⌉ can be used on atoms and canonical forms containing free binding variables. An expression without binding variables is called ground expression.

• If 𝑒 ′ ≡ 𝛼 𝑒 ′′ , then 𝑒 ′′ {𝑒/𝑒 ′ } = 𝑒. • If 𝑒 ′ ̸ ≡ 𝛼 𝑒 ′′ , then 𝑒 ′′ {𝑒/𝑒 ′ }
≡ 𝐸 ′ [𝑒 ′ 1 ] and 𝑒 ′ 1 ⊤ 𝑒 ′′ 2 for some 𝑒 ′′ 2 such that 𝐸 ′ [𝑒 ′′ 2 ] * C 𝐸 [𝑒 ′ 2 ]. Consequently, we have 𝑒 1 ≡ 𝐸 ′ [𝑒 ′ 1 ] 𝐸 ′ [𝑒 ′′ 2 ], and 𝐸 ′ [𝑒 ′′ 2 ] * C 𝐸 [𝑒 ′ 2 ] ≡ 𝑒 3 * C 𝑒 4 ,
Proposition I.51. For any ground expression of the source language 𝑒, ⌈term( 𝑒 )⌉ ≡ 𝑒.

Proof. Straightorward structural induction on 𝑒. □ Proposition I.52. If 𝜅 ≡ 𝜅 𝜅 ′ , then ⌈𝜅⌉ ≡ 𝛼 ⌈𝜅 ′ ⌉.

Proof. If a reordering, as defined in Definition 3.1, applies at top-level on the expression bind x 1 = 𝑎 1 in bind x 2 = 𝑎 2 in 𝜅 , the unwinding remains unchanged: as x 1 ∉ fv(𝑎 2 ) and 𝑥 2 ∉ fv(𝑎 1 ), we have 𝜅{𝑎 1 /x 1 }{𝑎 2 /x 2 } ≡ 𝜅{𝑎 2 /x 2 }{𝑎 1 /x 1 }.

The general case can easily be deduced with the observation that ∀C, 𝜅 1 , 𝜅 2 . ⌈𝜅 1 ⌉ ≡ 𝛼 ⌈𝜅 2 ⌉ ⇒ ⌈C [𝜅 1 ]⌉ ≡ 𝛼 ⌈C [𝜅 2 ]⌉ (with C denoting an arbitrary context). □ Proposition I.53 (Eqivalence of MSC-forms). If 𝜅 1 and 𝜅 2 are two MSC-forms and ⌈𝜅 1 ⌉ ≡ 𝛼 ⌈𝜅 2 ⌉, then 𝜅 1 ≡ 𝜅 𝜅 2 .

Proof. We will show that 𝜅 2 can be transformed into 𝜅 1 just with 𝛼-renaming and reordering of independent bindings (as specified in the definition of ≡ 𝜅 ).

In this proof, we represent partially unwinded canonical forms by a pair (𝐵, 𝑒), where 𝐵 is a binding context and 𝑒 an expression. With this representation, the unwinding of (𝐵, 𝑒) is 𝑒𝐵, but for clarity we can also use the notation ⌈(𝐵, 𝑒)⌉.

Let (𝐵 1 , x 1 ) be the representation of 𝜅 1 , with 𝐵 1 representing its top-level definitions and x 1 its final binding variable, and (𝐵 2 , x 2 ) be the representation of 𝜅 2 . Formally, we have term(𝐵 1 , x 1 ) ≡ 𝜅 1 and term(𝐵 2 , x 2 ) ≡ 𝜅 2 .

As ⌈𝜅 1 ⌉ ≡ 𝛼 ⌈𝜅 2 ⌉, we have ⌈(𝐵 1 , x 1 )⌉ ≡ 𝛼 ⌈(𝐵 2 , x 2 )⌉. By 𝛼-renaming, we can assume that x 1 = x 2 = x and ⌈(𝐵 1 , x)⌉ ≡ ⌈(𝐵 2 , x)⌉. Now, let's prove the property below, from which Proposition I.53 can be deduced. Let 𝐵 1 and 𝐵 2 two binding contexts, and 𝑒 an expression such that:

• ⌈(𝐵 1 , 𝑒)⌉ ≡ ⌈(𝐵 2 , 𝑒)⌉

• The body of 𝜆-abstractions in 𝐵 1 and 𝐵 2 are in MSC-form (Definition 3.2)

• Both 𝐵 1 and 𝐵 2 satisfy the following properties (corresponding to the MSC-form properties applied to the top-level definitions), written here for a binding context 𝐵:

(1) if (x 1 , 𝑎 1 ) and (x 2 , 𝑎 2 ) are distinct definitions in 𝐵, then 𝑎 1 ̸ ≡ 𝜅 𝑎 2

(2) for any definition (x, 𝜆𝑧.𝜅) in 𝐵, any binding bind y = 𝑎 in 𝜅 ′ in 𝜅 is such that fv(𝑎) ⊈ fv(𝜆𝑧.𝜅) (3) if 𝐵 contains a definition (x, 𝑎), then x is a free variable of one of the next definitions in 𝐵 or of 𝑒 Then, we can transform 𝐵 2 into 𝐵 1 just with 𝛼-renaming, reordering of independent definitions, and replacement of an atom by a ≡ 𝜅 -equivalent one.

We prove this property by induction on the total number of atoms appearing in 𝐵 1 (by counting top-level atoms as well as the sub-atoms they contain).

The base case (𝐵 1 = 𝜀) is trivial: as ⌈(𝐵 2 , 𝑒)⌉ ≡ ⌈(𝐵 1 , 𝑒)⌉ ≡ ⌈(𝜀, 𝑒)⌉ ≡ 𝑒, we deduce with Property 3 of MSC-forms that 𝐵 2 = 𝜀.

For the inductive case, let's consider 𝐵 1 = 𝐵 ′ 1 ; (x, 𝑎 1 ). With property 3 of MSC-forms, we know that x appears in 𝑒. As ⌈(𝐵 1 , 𝑒)⌉ ≡ ⌈(𝐵 2 , 𝑒)⌉, we can deduce that ⌈(𝐵 1 , x)⌉ ≡ ⌈(𝐵 2 , x)⌉. Thus, 𝐵 2 must also feature a definition for x, let's call 𝑎 2 the associated atom. We move in 𝐵 2 the definition (x, 𝑎 2 ) at the end (if not already), it gives 𝐵 2 = 𝐵 ′ 2 ; (x, 𝑎 2 ). We then have ⌈(𝐵 ′ 1 , 𝑎 1 )⌉ ≡ ⌈(𝐵 ′ 2 , 𝑎 2 )⌉. As every kind of atom introduces a different syntactic construction, we can deduce that 𝑎 1 and 𝑎 2 are atoms of the same kind.

• If 𝑎 1 and 𝑎 2 are atoms that are not 𝜆-abstractions and that do not contain any binding variable (constants, lambda variables), we directly have 𝑎 1 ≡ 𝑎 2 . • If 𝑎 1 and 𝑎 2 are atoms that are not 𝜆-abstractions and that contain only one binding variable (projections), we can 𝛼-rename binding variables in 𝐵 2 so that 𝑎 1 ≡ 𝑎 2 . • If 𝑎 1 and 𝑎 2 are atoms that are not 𝜆-abstractions and that contain two binding variables x and y (applications, pairs), we consider two cases:

-If ⌈(𝐵 ′ 1 , x)⌉ ≡ 𝛼 ⌈(𝐵 ′ 1 , y)⌉, let's show that we necessarily have x = y. We consider the binding context 𝐵 x , which is a cleaned version of 𝐵 ′ 1 where all the definitions that are not related (directly or indirectly) to x 1 have been removed. Similarly, we consider the binding context 𝐵 y where the definitions unrelated to y have been removed. Then, we apply the induction hypothesis to the binding contexts 𝐵 x , 𝐵 y {x/y} and the expression x. This tells us that 𝐵 x and 𝐵 y are equivalent modulo reordering of the definitions, 𝛼-renaming and ≡ 𝜅 -transformation of atoms. Thus, according to the property 1 of MSC-forms, x and y cannot have two distinct definitions in 𝐵 ′ 1 , and thus x = y. The same reasoning can be done for 𝑎 2 , and thus we deduce that both 𝑎 1 and 𝑎 2 contain the same binding variable twice. Thus, we can 𝛼-rename binding variables in 𝐵 2 so that 𝑎 1 ≡ 𝑎 2 .

-Otherwise, x and y must be different, and the same applies to 𝑎 2 . Thus, we can 𝛼-rename binding variables in 𝐵 2 so that 𝑎 1 ≡ 𝑎 2 .

• If 𝑎 1 and 𝑎 2 are typecases (containing 3 binding variables), we proceed similarly to the previous case to obtain 𝑎 1 ≡ 𝑎 2 . • In the case where 𝑎 1 and 𝑎 2 are 𝜆-abstractions, let's say 𝜆𝑥 . 𝜅 1 and 𝜆𝑥 . 𝜅 2 , we note (𝐵 x 1 , x 1 ) and (𝐵 x 2 , x 2 ) the representations of 𝜅 1 and 𝜅 2 respectively. We now consider the representation (𝐵 ′ 1 ; 𝐵 x 1 , 𝜆𝑥 . x 1 ) and remove from it all the unused definitions (i.e. not related to x 1 ), it gives us a new representation (𝐵 ′′ 1 , 𝜆𝑥 . x 1 ) . We do the same for (𝐵 ′ 2 ; 𝐵 x 2 , 𝜆𝑥 .

x 2 ), it gives a new representation (𝐵 ′′ 2 , 𝜆𝑥 . x 2 ). Then, we use the induction hypothesis on 𝐵 ′′ 1 , 𝐵 ′′ 2 {x 1 /x 2 } and 𝜆𝑥 . x 1 . It gives us that 𝐵 ′′ 1 and 𝐵 ′′ 2 are equivalent modulo reordering of the definitions, 𝛼-renaming and ≡ 𝜅 -transformation of the atoms. By using the property 2 of MSC-forms, we can deduce that 𝐵 x 1 and 𝐵 x 2 are equivalent modulo reordering of the definitions, 𝛼-renaming and ≡ 𝜅 -transformation of the atoms. Thus, we can 𝛼-rename 𝐵 2 and ≡ 𝜅 -transform some of its atoms so that 𝐵 x 1 ≡ 𝐵 x 2 , and thus 𝑎 1 ≡ 𝑎 2 . In any case, we get 𝑎 1 ≡ 𝑎 2 ≡ 𝑎, thus the last definition of 𝐵 1 is the same as the last definiton of 𝐵 2 . The same can be proven for the previous definitions by using the induction hypothesis on Proof. Let 𝑛 be the maximal number of nested 𝜆-abstractions in 𝜅 1 . We call depth of a binding the number of nested 𝜆-abstractions it is into (the depth of a binding of 𝜅 1 is at most 𝑛).

Let 𝑁 𝜅 (𝑖) be the number of bindings of depth 𝑖 in an expression 𝜅. Let 𝑆 (𝜅) be the following n-uplet: (𝑁 𝜅 (𝑛), 𝑁 𝜅 (𝑛 -1), . . . , 𝑁 𝜅 (0)).

For any chain 𝜅 1 𝜅 2 • • • , the sequence 𝑆 (𝜅 1 ), 𝑆 (𝜅 2 ), . . . is strictly decreasing with respects to the lexicographic order. Thus 𝜅 1 𝜅 2 • • • cannot be infinite. □ Proposition I.56. If 𝜅 ̸ (i.e., no rule apply on 𝜅), then 𝜅 is an MSC-form.

Proof. We assume 𝜅 ̸ and show that all 3 MSC properties are satisfied. The property 3 (no unused bindings) is trivially verified: any binding that does not satisfy this property can directly be eliminated with the rule 4. As the rule 4 does not apply, this property must be satisfied. Now, we focus on the property 2 (extrusion of bindings). We assume that there exists a subexpression 𝜆𝑥 . 𝜅 1 of 𝜅 and a sub-expression bind y = 𝑎 in 𝜅 2 of 𝜅 1 such that fv(𝑎) ⊆ fv(𝜆𝑥 . 𝜅 1 ). We know that 𝑎 does not depend on 𝑥, otherwise 𝑥 would be in fv(𝑎) and not in fv(𝜆𝑥 . 𝜅 1 ). Thus, we can reorder the binding 𝑦 (6) in the first position of its inner-most containing lambda-abstraction, and then apply the rule 5 on it, which contradicts 𝜅 ̸ . Consequently, the property 2 is satisfied.

Finally, we show that the property 1 (sharing) is also satisfied. We assume that there are two distinct bindings bind x 1 = 𝑎 1 in . . . and bind x 2 = 𝑎 2 in . . . such that 𝑎 1 ≡ 𝜅 𝑎 2 . As the property 2 is satisfied, and as fv(𝑎 1 ) = fv(𝑎 2 ), we know that these two bindings are in the same 𝜆-abstraction. Thus, we can reorder them [START_REF] Henglein | Type Inference with Polymorphic Recursion[END_REF] to be the one next to the other so that the rule 3 is applicable, which contradicts 𝜅 ̸ . Thus, the property 1 is satisfied. □ Proposition I.57 (Confluence). Let 𝜅 1 , 𝜅 2 and 𝜅 ′ 2 such that 𝜅 1 * 𝜅 2 and 𝜅 1 * 𝜅 ′ 2 . Then, there exists 𝜅 3 and 𝜅 ′ 3 such that 𝜅 2 * 𝜅 3 , 𝜅 ′ 2 * 𝜅 ′ 3 , and 𝜅 3 ≡ 𝜅 𝜅 ′ 3 . Proof. Immediate consequence of Proposition I.55 (normalisation), Proposition I.54 (preservation of ⌈.⌉), Proposition I.56 (̸ implies MSC-form), and Proposition I.53 (equivalence of MSCforms). □ need to prove that the annotation @(Σ 1 , Σ 2 ) generated by the [App] root satisfies Γ(x 1 )Σ 1 ≤ 0 → 1 and Γ(x 2 )Σ 2 ≤ dom(Γ(x 1 )Σ 1 ).

We have, in the premise of the [App] root, Σ = tally({Γ(x 1 )𝜌 1 ≤ Γ(x 2 )𝜌 2 → 𝛼 }) and Σ ≠ ∅. Let 𝜎 ∈ Σ. By definition of the tallying problem, we have (Γ(x 1 )𝜌 1 )𝜎 ≤ (Γ(x 2 )𝜌 2 → 𝛼)𝜎, which can be rewritten Γ(x 1 ) (𝜎 • 𝜌 1 ) ≤ (Γ(x 2 ) (𝜎 • 𝜌 2 )) → (𝛼𝜎). From that subtyping relation, we can deduce Γ(x 1 ) (𝜎 • 𝜌 1 ) ≤ 0 → 1, and by definition of dom(.), Γ(x 2 ) (𝜎 • 𝜌 2 ) ≤ dom(Γ(x 1 ) (𝜎 • 𝜌 1 )). As (𝜎 • 𝜌 2 ) ∈ Σ 2 and (𝜎 • 𝜌 1 ) ∈ Σ 1 , we deduce Γ(x 1 )Σ 1 ≤ 0 → 1 and Γ(x 2 )Σ 2 ≤ dom(Γ(x 1 )Σ 1 ) (we use the fact that dom(.) is monotonically non-increasing).

The other cases are similar or straightforward. □

Now, we propose a sketch of proof justifying that the deduction rules for the reconstruction system define a terminating algorithm. The idea of this proof is similar to the proof of termination of the Kirby-Paris hydra game12 : we can associate an ordinal number weight to each node, and this weight can only decrease as the game (or derivation) advances. Intuitively, this non-negative weight represents the advancement of the game (or derivation). Though sub-trees can sometimes be duplicated, their weight is always lowered before being duplicated, resulting in a lower weight overall.

For any type environment Γ, canonical form or atom 𝜂, and intermediate annotation H compatible with the structure of 𝜂, the weight w(Γ, 𝜂, H ) is the ordinal number defined as follows:

7

  function lOr (x, y) { 8 if ( ToBoolean (x)) { return x; } else { return y; } 9 }

Syntax

  Test Type 𝜏 ::= 𝑏 | 0 → 1 | 𝜏 × 𝜏 | 𝜏 ∨ 𝜏 | ¬𝜏 | 0 Expression 𝑒 ::= 𝑐 | 𝑥 | 𝜆𝑥 .𝑒 | 𝑒𝑒 | (𝑒, 𝑒) | 𝜋 𝑖 𝑒 | (𝑒∈𝜏) ? 𝑒 : 𝑒 Value 𝑣 ::= 𝑐 | 𝜆𝑥 .𝑒 | (𝑣, 𝑣) Program 𝑝 ::= let 𝑥 = 𝑒 ; 𝑝 | 𝑒 Reduction rules (𝜆𝑥 .𝑒)𝑣 𝑒{𝑣

Fig. 3 .

 3 Fig. 3. MSC-form and two annotations for 𝜆𝑥 .(𝑓 𝑥 ∈Int) ? 𝑔(𝑓 𝑥) : 𝑥 one on the right. This intersection annotation (line 2) separates the domain of the 𝜆-abstraction into two cases, each typed independently, yielding for the whole function an intersection type.The example in Figure3also shows why the condition of maximal sharing for our forms is necessary, not only for their uniqueness, but also for the completeness of the algorithmic system: if the two occurrences of 𝑓 𝑥 in "𝜆𝑥 .(𝑓 𝑥 ∈Int) ? 𝑔(𝑓 𝑥) : 𝑥" were not bound by the same variable (as in the leftmost column of line 5 in Figure3), viz., if the sharing were not maximal, then it would not be possible to deduce that 𝑔(𝑓 𝑥) is well typed: 𝑔 expects an integer, but without maximal sharing it is not possible to deduce that the occurrence of 𝑓 𝑥 in the first branch is indeed of type Int.The problem of inferring an annotation for an MSC-form as the above-in particular the rightmost (more precise) annotation in Figure3-is tackled in the next section.

Theorem 4 .

 4 2 (Soundness). If Γ ⊢ P ⟨𝜅 | K⟩ ⇒ k, then ∃𝑡 . Γ ⊢ A [𝜅 | k] : 𝑡.Theorem 4.3 (Termination). The deduction rules ⊢ * R and ⊢ R define a terminating algorithm: it can either fail (if no rule applies at some point) or return a result R.

2

  if x is Truthy then true else false let lOr (x ,y) = if toBoolean x then x else y let id x = lOr (x , x ) (Falsy → False) ∧ (Truthy → True)( ( (𝛼 ∧ Truthy) × 1) → 𝛼 ∧ Truthy)∧ ( (Falsy × 𝛽) → 𝛽) let fixpoint = fun f -> let delta = fun x -> f ( fun v -> ( x x v )) in delta delta ( (𝛽 → 𝛼) → (𝛽 → 𝛼) ∧ 𝛾 ) → (𝛽 → 𝛼) ∧ 𝛾 15.37 3 let map_stub map f lst = if lst is Nil then nil else (f ( fst lst ) , map f ( snd lst )) let map = fixpoint map_stub . . . ( (𝛼 → 𝛽) → ( [𝛼 * ] → [𝛽 * ])) ∧ (1 → [] → []) filter (f: ( 'a -> Any ) & (' b -> ~True )) (l: [( ' a|' b )*]) = if l is Nil then nil else if f( fst ( l )) isTrue then ( fst (l), filter f ( snd ( l ))) else filter f ( snd ( l )) let filter = fixpoint filter_stub . . . ( (𝛼 → 1)∧(𝛽 → ¬True)) → [ (𝛼 ∨ 𝛽) * ]→[ (𝛼∖𝛽) * ] flatten x = match x with |

d

  𝑥 (𝑝, 𝑒) = undefined otherwise 40:37 B FULL SEMANTICS WITH EXTENSIONS Expressions of the source language with extensions of Appendix A are defined as follows: Test Types 𝜏 ::= 𝑏 | 0 → 1 | 𝜏 × 𝜏 | 𝜏 ∨ 𝜏 | ¬𝜏 | 0 Patterns 𝑝 ::= 𝜏 | 𝑥 | 𝑝&𝑝 | 𝑝|𝑝 | (𝑝, 𝑝) | 𝑥 := 𝑐 Expressions 𝑒 ::= 𝑐 | 𝑥 | 𝜆𝑥 .𝑒 | 𝑒𝑒 | (𝑒, 𝑒) | 𝜋 𝑖 𝑒 | (𝑒∈𝜏) ? 𝑒 : 𝑒 | let 𝑥 = 𝑒 in 𝑒 | (𝑒 : 𝜏) | (tcase 𝑒 of 𝜏 → 𝑒 | . . . | 𝜏 → 𝑒) | (match 𝑒 with 𝑝 → 𝑒 | . . . | 𝑝 → 𝑒) Values 𝑣 ::= 𝑐 | 𝜆𝑥 .𝑒 | (𝑣, 𝑣) The associated reduction rules are:

  where the evaluation contexts 𝐸 [] are defined as follows: Evaluation Context 𝐸 ::= [ ] | 𝑣𝐸 | 𝐸𝑒 | (𝑣, 𝐸) | (𝐸, 𝑒) | 𝜋 𝑖 𝐸 | (𝐸∈𝜏) ? 𝑒 : 𝑒 | let 𝑥 = 𝐸 in 𝑒 | (𝐸 : 𝜏) | (tcase 𝐸 of 𝜏 → 𝑒 | . . . | 𝜏 → 𝑒) | (match 𝐸 with 𝑝 → 𝑒 | . . . | 𝑝 → 𝑒)

  𝜆 and ranged over by 𝑥, 𝑦, and 𝑧. The latters are called binding variables, the set of all binding variables is denoted by Vars B and ranged over by x, y, and z. Vars 𝜆 and Vars B form a partition of the set of variables Vars (formally, Vars = Vars 𝜆 ∪Vars B ). The syntax of expressions and the rules of the type system are changed accordingly as follows: Expressions 𝑒 ::= 𝑐 | 𝑥 | x | 𝜆𝑥 .𝑒 | 𝑒𝑒 | (𝑒, 𝑒) | 𝜋 𝑖 𝑒 | (𝑒∈𝜏) ? 𝑒 : 𝑒 [Ax 𝜆 ] 𝑒 ′ : 𝑠 (∀𝑖 ∈ 𝐼 ) Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 Γ ⊢ : 𝑒{𝑒 ′ /x} : 𝑡 {u 𝑖 } 𝑖 ∈𝐼 ∈ Part(1)

  𝑒 ′ : 𝑠 𝐵 𝑖 Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 ∀𝑖 ∈ 𝐼 Γ ⊢ : 𝑒{𝑒 ′ /x} : 𝑡 40:59

  Given that the ∈ relation ignores the body of 𝜆-abstractions ((𝜆𝑥 .𝑒) ∈ 0 → 1 for any 𝑒), none of the reduction steps in 𝑣 1 * C 𝑣 2 can change the relation • ∈ 𝜏. □ Theorem I.50 (Type safety for the source semantics). For any program 𝑝, if ∅ ⊢ Pr 𝑝 : 𝜏, then either 𝑝 * Pr 𝑣 with 𝑣 ∈ 𝜏 or 𝑝 ∞ Pr . Proof. Straightforward combination of the type safety for the parallel semantics (Theorem I.40) with the Lemmas I.47 and I.48. In the case where we get 𝑝 * Pr 𝑣, for some value 𝑣 such that there exists 𝑣 ′ such that 𝑣 * C 𝑣 ′ and ∅ ⊢ Pr 𝑣 ′ : 𝜏, we can deduce ∅ ⊢ : 𝑣 ′ : 𝜏, then 𝑣 ′ ∈ 𝜏 using Proposition I.23, and finally 𝑣 ∈ 𝜏 using Lemma I.49. □ I.3 Algorithmic Type System I.3.1 Maximal Sharing Canonical Form. This section applies to definitions of Appendix E.

  w(Γ, 𝜂, typ) = 1 w(Γ, 𝜂, untyp) = 1 w(Γ, 𝜂, (𝑆 1 , 𝑆 2 )) = {w(Γ, 𝜂, H ) | H ∈ 𝑆 1 } w(Γ, 𝑐, infer) = 𝜔 w(Γ, 𝑥, infer) = 𝜔 w(Γ, 𝜆𝑥 .𝜅, infer) = 𝜔 w(Γ,𝜅,infer) w(Γ, 𝜆𝑥 .𝜅, 𝜆(u, K)) = 𝜔 w(Γ,𝜅,K) w(Γ, 𝜋 𝑖 x, infer) = 𝜔 if x ∈ dom(Γ) w(Γ, 𝜋 𝑖 x, infer) = 𝜔 2 otherwise w(Γ, x 1 x 2 , infer) = 𝜔 if {x 1 , x 2 } ⊆ dom(Γ) w(Γ, x 1 x 2 , infer) = 𝜔 2 otherwise, if x 1 ∈ dom(Γ) w(Γ, x 1 x 2 , infer) = 𝜔 2 otherwise, if x 2 ∈ dom(Γ) w(Γ, x 1 x 2 , infer) = 𝜔 3 otherwise w(Γ, (x 1 , x 2 ), infer) = 𝜔 if {x 1 , x 2 } ⊆ dom(Γ) w(Γ, (x 1 , x 2 ), infer) = 𝜔 2 otherwise, if x 1 ∈ dom(Γ) w(Γ, (x 1 , x 2 ), infer) = 𝜔 2 otherwise, if x 2 ∈ dom(Γ)w(Γ, (x 1 , x 2 ), infer) = 𝜔 3 otherwise

  [Iterate 1 ], or they return substitutions that do not affect the current environment (i.e., 𝜓 𝑖 #Γ) as in rule [Iterate 2 ]. For the latter rule, the iteration may need to introduce an intersection annotation (useless when 𝐼 is empty) in order to explore all the cases of a Subst({𝜓 𝑖 } 𝑖 ∈𝐼 , H 1 , H 2 ) result (where H𝜓 is the intermediate annotation H in which the substitution 𝜓 has been applied recursively to every type in it). An important special case of the [Iterate 2 ] rule is when 𝐼 = ∅: in that case the iteration continues by trying to type 𝜂 with the default annotation H 2 and the current environment Γ. For instance, this special case triggers a [CaseVar 1 ] after a [CaseThen] and a [CaseVar 2 ] after a[CaseElse].If the result is already terminal or if it is not immediately usable, then it is directly returned:

[Stop]

  𝑖 ∈𝐼 𝑡 𝑖 ≃ 𝑡, (𝑖𝑖) ∀𝑖 ∈ 𝐼 . 𝑡 𝑖 0, and (𝑖𝑖𝑖) ∀𝑖, 𝑗 ∈ 𝐼 . 𝑖 ≠ 𝑗 ⇒ 𝑡 𝑖 ∧ 𝑡 𝑗 ≃ 0. The guard condition is most of time omitted, for concision. This allows to combine successive [∨] rule applications substituting the same sub-expression into one [∨] rule, making the normalisation lemmas easier to express. Again, this new [∨] rule is admissible. For instance, the following derivation: 𝑥 : 𝑠 ∧ ¬u 1 , 𝑦 : 𝑠 ∧ u 2 ⊢ : 𝑒{𝑦/𝑥 } : 𝑡 𝐷 {𝑦/𝑥 } Γ, 𝑥 : 𝑠 ∧ ¬u 1 , 𝑦 : 𝑠 ∧ u 3 ⊢ : 𝑒{𝑦/𝑥 } : 𝑡 Γ, 𝑥 : 𝑠 ∧ ¬u 1 ⊢ : (𝑒{𝑦/𝑥 }){𝑥/𝑦} : 𝑡 This construction can be generalized for a partition of 1 of any cardinality. Lastly, we distinguish variables that are introduced by a [→I] node from variables introduced by a [∨] node. The formers are called lambda variables, the set of all lambda variables is denoted by Vars

			𝐴		𝐵	𝐶	𝐷
		[∨]	Γ ⊢ : 𝑒 ′ : 𝑠	Γ, 𝑦 : 𝑠 ∧ u 1 ⊢ : 𝑒 : 𝑡 Γ ⊢ : 𝑒{𝑒 ′ /𝑥 } : 𝑡 Γ, 𝑦 : 𝑠 ∧ u 2 ⊢ : 𝑒 : 𝑡	Γ, 𝑦 : 𝑠 ∧ u 3 ⊢ : 𝑒 : 𝑡
	can be transformed to use only two binary [∨] rules:
				𝐴	𝐵	𝑋
			[∨]	Γ ⊢ : 𝑒 ′ : 𝑠	Γ, 𝑥 : 𝑠 ∧ u 1 ⊢ : 𝑒 : 𝑡 Γ ⊢ : 𝑒{𝑒 ′ /𝑥 } : 𝑡 Γ, 𝑥 : 𝑠 ∧ ¬u 1 ⊢ : 𝑒 : 𝑡
				with 𝑋 being the following derivation:
				𝐶{𝑦/𝑥 }
	[∨]	[Ax]	Γ,

  We introduce for convenience a new notation that takes the form of a new rule[Inst∧≤], but is actually just a shortand for a specific combination of[Inst], [∧], and [≤] rules: For derivation 𝐷 of Γ ⊢ : 𝑒 : 𝑡 and environment Γ ′ such that Γ ′ ≤ P Γ, 𝐷 can be transformed into a derivation of Γ ′ ⊢ : 𝑒 : 𝑡 just by adding [≤], [Inst] and [∧] nodes. Proof. Straightforward induction on the derivation Γ ⊢ : 𝑒 : 𝑡, where each [Ax ∨ ] and [Ax 𝜆 ] node is replaced by a [Inst∧≤] pattern of that node. □ I.1.2 Normalisation Lemmas. Derivations for the declarative type system of Figure4can still take many different shapes. In this section, we define several normalisation lemmas, each restricting the use of a non-structural rule. They are then combined into a normalisation theorem.Normalisation of [∨] nodesLemma I.5 (Introduction of an arbitrary [∨] node). Let Γ a type environment, 𝑒 and 𝑒 x two expressions, and {u 𝑖 } 𝑖 ∈𝐼 a partition of 1. Let 𝐷 be a derivation for the judgement Γ ⊢ : 𝑒{𝑒 x /x} : 𝑡 such that 𝐷 does not contain any [∨] node performing a substitution {𝑒 y /y} with 𝑒 y a strict sub-expression of 𝑒 x . If 𝑒 x is typable under the context Γ, then there exists a type 𝑠 such that 𝐷 can be transformed into a derivation whose root is a [∨] node of the following form:

							𝐴
	[Inst∧≤]	𝐴 : 𝑒 : 𝑡 ′ Γ ⊢	(∃Σ. 𝜎 ∈Σ 𝑡 ′ 𝜎 ≤ 𝑡) Γ ⊢ : 𝑒 : 𝑡	↔ [≤]	[∧]	[Inst] Γ ⊢ Γ ⊢ : 𝑒 : 𝑡 ′ Γ ⊢ : 𝑒 : 𝑡 ′ 𝜎 : 𝑒 : 𝜎 ∈Σ 𝑡 ′ 𝜎 ∀𝜎 ∈ Σ Γ ⊢ : 𝑒 : 𝑡
	. . . Γ ⊢ Lemma I.4 (Monotonicity). [∨] : 𝑒 x : 𝑠	. . . Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡	∀𝑖 ∈ 𝐼
				Γ ⊢ : 𝑒{𝑒 x /x} : 𝑡

  • 𝑒 does not contain 𝑒 ′ (maximal sharing), and Proof. For each 𝑖 ∈ 𝐼 , let Σ 𝑖 a set of substitutions such that 𝑡 ′ 𝑖 Σ 𝑖 ≤ 𝑡 𝑖 . We consider the set of substitutions Σ = 𝑖 ∈𝐼 Σ 𝑖 , and we show that ( 𝑖 ∈𝐼 𝑡 ′ 𝑖 )Σ ≤ 𝑖 ∈𝐼 𝑡 𝑖 . We have:𝜎 ∈Σ ( 𝑖 ∈𝐼 𝑡 ′ 𝑖 )𝜎 ≃ 𝑖 ∈𝐼 𝜎 ∈Σ 𝑖 ( 𝑗 ∈𝐼 𝑡 ′ 𝑗 )𝜎 ≤ 𝑖 ∈𝐼 𝜎 ∈Σ 𝑖 𝑡 ′ 𝑖 𝜎 ≤ 𝑖 ∈𝐼 𝑡 𝑖 □ Proposition I.16. If ∀𝑖 ∈ 𝐼 . 𝑡 ′ 𝑖 ≤ P 𝑡 𝑖 and all {𝑡 𝑖 } 𝑖 ∈𝐼 have disjoint polymorphic type variables, then 𝑖 ∈𝐼 𝑡 ′ 𝑖 ≤ P 𝑖 ∈𝐼 𝑡 𝑖 . Proof. For each 𝑖 ∈ 𝐼 , let Σ 𝑖 a set of substitutions such that 𝑡 ′ 𝑖 Σ 𝑖 ≤ 𝑡 𝑖 . We consider the set of substitutionsΣ = {𝜎 1 ∪ . . . ∪𝜎 𝑛 | 𝜎 1 ∈ Σ 1 , . . . , 𝜎 𝑛 ∈ Σ 𝑛 } for 𝐼 = {1, .. . , 𝑛}, where ∪ denotes the composition of disjoint substitutions (their disjointness is guaranteed by the fact that all {𝑡 ′ 𝑖 } 𝑖 ∈𝐼 have disjoint polymorphic type variables), and we show that ( 𝑖 ∈𝐼 𝑡 ′ 𝑖 )Σ ≤ 𝑖 ∈𝐼 𝑡 𝑖 . We have:

  𝜎 𝑖 ∈Σ 𝑖 𝑡 ′ 𝑖 𝜎 𝑖 (distributivity of ∨ over ∧) ≤ 𝑖 ∈1. .𝑛 𝑡 𝑖 𝑡 ′ for the order ≤ and with 𝑡 ′ ≤ P 𝑡.

	□
	Definition I.17 ([Inst]-canonical derivation). A derivation 𝐷 is [Inst]-canonical if every
	[Inst] node it contains is part of a [Inst∧≤] pattern that is either:

• The first premise of a [0], [∈ 1 ] or [∈ 2 ] node, or • The premise of a [×E 1 ] or [×E 2 ] node, or • One of the premises of a [→E] node Lemma I.18 (Normalisation of [Inst]). Given an expression order ≤, any [∨]-canonical form derivation 𝐷 of Γ ⊢ : 𝑒 : 𝑡 can be transformed into a [∨][Inst]-canonical form derivation of Γ ⊢ : 𝑒 :

  x} : 𝑡

	The other cases are straightforward.	□
	Normalisation of [≤] nodes
	Definition I.19 ([≤]-canonical derivation). A derivation 𝐷 is [≤]-canonical if every [≤] node
	it contains is either:
	• The first premise of a [∈ 1 ] or [∈ 2 ] node, or
	• One of the body premises of a [∨] node, or
	• The premise of a [×E 1 ] or [×E 2 ] node, or

• The first premise of a [→E] node Lemma I.20 (Normalisation of [≤]). Given an expression order ≤, any [∨][Inst]-canonical form derivation 𝐷 of Γ ⊢ : 𝑒 : 𝑡 can be transformed into a [∨][Inst][≤]-canonical form derivation of Γ ⊢ : 𝑒 : 𝑡 ′ for the order ≤ and with 𝑡 ′ ≤ P 𝑡.

  is inductively defined as Proof. Let 𝑒 1 and 𝑒 4 two expressions such that 𝑒 1 * C 𝑒 4 , where at least one of the reduction steps happen under an evaluation context.Let's consider the first reduction step 𝑒 2 C 𝑒 3 happening under an evaluation context. We have 𝑒 1 * C 𝑒 2 (with no reduction step happening under an evaluation context) and 𝑒 3 * C 𝑒 4 . Moreover, we have 𝑒 2 ≡ 𝐸 [𝑒 ′ 2 ] and 𝑒 3 ≡ 𝐸 [𝑒 ′ 3 ] for some evaluation context 𝐸 and expressions 𝑒 ′ 𝑒 2 happen under a context C such that C ≤ 𝐸: otherwise, it would also be an evaluation context, contradicting the fact that 𝑒 2 C 𝑒 3 is the first reduction step happening under an evaluation context. Consequently, we can "reverse" in 𝐸 and 𝑒 ′ 2 the reduction steps happening in 𝑒 1 * C 𝑒 2 (one after the other, in reverse order):• If a reduction step happens under a context C such that 𝐸 ≤ C, it only involves 𝑒 ′ 2 , we can thus apply the reverse rewriting to 𝑒 ′ 2 . After that, the expression we get is still reducible at top-level, as the reduction step that has been reversed cannot happen under an evaluation context (no reduction step in 𝑒 1 * C 𝑒 2 can happen under an evaluation context).• Otherwise, if a reduction step happens under a context C such that C ̸ ≤ 𝐸 and 𝐸 ̸ ≤ C, it only involves 𝐸, we can thus apply the reverse rewriting to 𝐸. After that, the new context we get is still an evaluation context, as the reduction step that has been reversed cannot happen under an evaluation context (no reduction step in 𝑒 1 * C 𝑒 2 can happen under an evaluation context). After this reversing process, we get a new evaluation context 𝐸 ′ and expression 𝑒 ′ 1 such that 𝑒 1

	that 𝑒 ′ 2 No reduction step in 𝑒 1 ⊤ 𝑒 ′ 3 .	* C	2 and 𝑒 ′ 3 such

  which concludes the proof. □ Lemma I.43. For any expressions 𝑒 1 , 𝑒 2 and 𝑒 3 , if 𝑒 1 * Proof. The step 𝑒 2 P 𝑒 3 can be decomposed into several C steps with at least one happening under an evaluation context. Thus, this lemma is an immediate consequence of Lemma I.42 applied on the chain 𝑒 1 * C 𝑒 2 * C 𝑒 3 . □ Lemma I.44. For any expression 𝑒 and value 𝑣, if 𝑒 * C 𝑣 then either 𝑒 ∞ or there exists a value 𝑣 ′ such that 𝑒 * 𝑣 ′ * C 𝑣. Proof. If 𝑒 is not already a value, then there must be at least one step in 𝑒 * C 𝑣 that happen under an evaluation context (it is not possible for 𝑣 to be a value otherwise). We can thus apply Lemma I.42 successively, starting on 𝑒 * C 𝑣 and continuing until the remaining 𝑒 ′ * C 𝑣 chain is such that 𝑒 ′ is a value. If this process terminates, it builds a chain 𝑒 * 𝑣 ′ with 𝑣 ′ * C 𝑣, otherwise it builds 𝑒 ∞ . □ Lemma I.45. For any expression 𝑒 1 , 𝑒 2 , and value 𝑣, if 𝑒 1 * C 𝑒 2 * P 𝑣 then there exists 𝑣 ′ such that 𝑒 1 * 𝑣 ′ * C 𝑣 or 𝑒 1 ∞ . Proof. By induction on the number of steps in 𝑒 2 * P 𝑣, we prove using Lemma I.43 that 𝑒 1 * 𝑒 ′ * C 𝑣 for some 𝑒 ′ . Then, we conclude by applying Lemma I.44 on 𝑒 ′ * C 𝑣. □ Lemma I.46. For any expression 𝑒 1 and 𝑒 2 , if 𝑒 1 * The semantics C is extended into a semantics C,Pr for programs, with an extended context allowing to perform a reduction under any top-level definition of the program: Pr::= [] | let 𝑥 = [] ; 𝑝 | let 𝑥 = 𝑒 ; C Pr 𝑒 ⊤ 𝑒 ′ C Pr [C [𝑒]] C,Pr C Pr [C [𝑒 ′ ]] Lemma I.47. For any program 𝑝 1 , 𝑝 2 , and value 𝑣, if 𝑝 1 * C,Pr 𝑝 2 * P,Pr 𝑣 then there exists 𝑣 ′ such that 𝑝 1 * Pr 𝑣 ′ * C,Pr 𝑣 or 𝑝 1 ∞ Pr . Proof. Straightforward induction on the number of top-level definitions in 𝑝 2 , using Lemma I.45 (note that 𝑝 1 and 𝑝 2 must have the same number of top-level definitions as 𝑝 1 * C,Pr 𝑝 2 ). □ Lemma I.48. For any program 𝑝 1 and 𝑝 2 , if 𝑝 1 * Proof. Straightforward induction on the number of top-level definitions in 𝑝 2 , using Lemma I.46 (note that 𝑝 1 and 𝑝 2 must have the same number of top-level definitions as 𝑝 1 * C,Pr 𝑝 2 ). □ Lemma I.49. For any values 𝑣 1 and 𝑣 2 such that 𝑣 1 * C 𝑣 2 , if 𝑣 2 ∈ 𝜏 then 𝑣 1 ∈ 𝜏. Proof. As 𝑣 1 is a value, every reduction step in 𝑣 1 * C 𝑣 2 can only happen under a 𝜆-abstraction.

					40:71
	C C,Pr 𝑝 2	∞ P,Pr then 𝑝 1	∞ Pr .
	𝑒 ′ 1 such that 𝑒 1	𝑒 ′ 1	* C 𝑒 3 .	C 𝑒 2 P 𝑒 3 , then there exists an expression

C 𝑒 2 ∞ P then 𝑒 1 ∞ .

Proof. We can arbitrarily extend a chain 𝑒 1 . . . using Lemma I.43. □

  𝐵 ′ 1 , 𝐵 ′ 2 and 𝑒{𝑎/x}. □ Proposition I.54. If 𝜅 𝜅 ′ , then ⌈𝜅⌉ ≡ 𝛼 ⌈𝜅 ′ ⌉. Proof. Similar to Proposition I.52. □ Proposition I.55 (Normalisation). There is no infinite chain 𝜅 1 𝜅 2 • • •

This type can be considered as an encoding of ∀(𝛼 ≤ Truthy).∀(𝛽) . ( (𝛼, Any) → 𝛼) ∧ ( (Falsy, 𝛽) → 𝛽) a type expressed in so-called bounded polymorphism: seeCastagna [2023a, Section 2].

Strictly speaking, the type states that the function returns a result of the same type as the first argument, but by parametricity we can deduce that the result will be the first argument. Likewise for the second argument. A simple way to understand it, is by instantiating both type variables in (2) with the singleton type of the (value result of the) argument.

A rule is analytic (as opposed to synthetic) when the input (i.e., Γ and 𝑒) of the judgment at the conclusion is sufficient to determine the inputs of the judgments at the premises (cf.[START_REF] Martin-Löf | Analytic and Synthetic Judgements in Type Theory[END_REF] Types 2019]).

Precisely, it deduces for it the type (𝛼∧Int → 𝛼∧Int) ∧ (𝛽∖Int → 𝛽∖Int). Instantiating 𝛽 to 𝛼 yields a subtype of 𝛼 → 𝛼.

The term polytypes and monotypes can be found (albeit inconsistently) in the literature: in particular,[START_REF] Milner | A theory of type polymorphism in programming[END_REF] uses the latter to denote types with no type variables and the former when he wishes to imply that a type may, or does, contain a variable. We avoided using them to prevent any confusion with our monomorphic types. While our types are indeed polytypes, our monomorphic types are not monotypes: monotypes do not have type variables while monomorphic types may have type variables, though only monomorphic ones. So we used instead types (which may have type variables), ground types (which cannot have any type variable), and monomorphic types (which may have monomorphic type variables, only).40:11[Const] Γ ⊢ 𝑐 : b 𝑐 [Ax] Γ ⊢ 𝑥 : Γ(𝑥) [→I] Γ, 𝑥 : u ⊢ 𝑒 : 𝑡 Γ ⊢ 𝜆𝑥 .𝑒 : u → 𝑡 [→E] Γ ⊢ 𝑒 1 : 𝑡 1 → 𝑡 2 Γ ⊢ 𝑒 2 : 𝑡 1 Γ ⊢ 𝑒 1 𝑒 2 : 𝑡 2 [×I] Γ ⊢ 𝑒 1 : 𝑡 1 Γ ⊢ 𝑒 2 : 𝑡 2 Γ ⊢ (𝑒 1 , 𝑒 2 ) : 𝑡 1 × 𝑡 2 [×E 1 ] Γ ⊢ 𝑒 : 𝑡 1 × 𝑡 2 Γ ⊢ 𝜋 1 𝑒 : 𝑡 1 [×E 2 ] Γ ⊢ 𝑒 : 𝑡 1 × 𝑡 2 Γ ⊢ 𝜋 2 𝑒 : 𝑡 2 [0] Γ ⊢ 𝑒 : 0 Γ ⊢ (𝑒∈𝜏) ? 𝑒 1 : 𝑒 2 : 0 [∈ 1 ] Γ ⊢ 𝑒 : 𝜏 Γ ⊢ 𝑒 1 : 𝑡 1 Γ ⊢ (𝑒∈𝜏) ? 𝑒 1 : 𝑒 2 : 𝑡 1 [∈ 2 ] Γ ⊢ 𝑒 : ¬𝜏 Γ ⊢ 𝑒 2 : 𝑡 2 Γ ⊢ (𝑒∈𝜏) ? 𝑒 1 : 𝑒 2 : 𝑡 2 [∨] Γ ⊢ 𝑒 ′ : 𝑠 Γ, 𝑥 : 𝑠 ∧ u ⊢ 𝑒 : 𝑡 Γ, 𝑥 : 𝑠 ∧ ¬u ⊢ 𝑒 : 𝑡 Γ ⊢ 𝑒{𝑒 ′ /𝑥 } : 𝑡 [∧] Γ ⊢ 𝑒 : 𝑡 1 Γ ⊢ 𝑒 : 𝑡 2 Γ ⊢ 𝑒 : 𝑡 1 ∧ 𝑡 2 [Inst]

For instance, both 𝜆𝑥 .bind x = 𝑥 in bind z = xy in bind z ′ = zy in z ′ and 𝜆𝑥 .bind x = 𝑥 in bind z = xy in z are two distinct atoms that can occur in the same MSC-form, even though the atom xy appears in both: an 𝛼-renaming of x makes the first MSC-property hold.

As stated by Theorem 3.4 the transformation of an expression into its MSC-form preserves typing. However, intuitively, it does not preserve the reduction semantics, since bindings regroup different occurrences of some sub-expression that in the original expression might be evaluated at different stages of the reduction or not evaluated at all. We said "intuitively" because no operational semantics is defined for canonical forms (this was alredy the case for[Castagna et al. 2022b]).

In an annotation keep (A, S, S ′ ) for the binding of a variable x, A is the annotation for typing the definition of x, while the two other arguments describe the type decomposition to use for x and, for each part of the decomposition, the annotation to use for the body. More precisely, S contains the parts of the type decomposition that have yet to be explored, and S ′ contains the parts that have already been fully explored. In particular, the annotation keep (A ′ , {(1, K 1 )}, ∅) used in rule [BindTryKeep 1 ] means that the type of the definition does not need to be partitioned: there is only one part, covering 1, associated with an annotation K 1 for typing the body.[BindOk]Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | keep (A, ∅, S)⟩ ⇒ Ok(keep (A, ∅, S))If all the parts of the type decomposition have already been explored (i.e., ∅ in the annotation in the rule above), then the reconstruction is successful. Otherwise, the following rules are applied:[BindKeep 1 ] Γ ⊢ P ⟨𝑎 | A⟩ ⇒ a Γ ⊢ A [𝑎 | a] : 𝑠 Γ, x : 𝑠 ∧ u ⊢ * R ⟨𝜅 | K⟩ ⇒ Ok(K ′ ) Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | keep (A, S, {(u, K ′ )} ∪ S ′ )⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | keep (A, {(u, K)} ∪ S, S ′ )⟩ ⇒ R [BindKeep 2 ] Γ ⊢ P ⟨𝑎 | A⟩ ⇒ a Γ ⊢ A [𝑎 | a] : 𝑠 Γ, x : 𝑠 ∧ u ⊢ * R ⟨𝜅 | K⟩ ⇒ Split(Γ ′ , K 1 , K 2 ) x ∈ dom(Γ ′ )Γ ⊢ E (𝑎 : ¬(u ∧ Γ ′ (x))) ⇒ L

The solution is not interesting since it is the one that allows any simply typed system to type all pure lambda terms. We do not need this recursive type to type, say, the application of the polymorphic identity function to itself.

In a nutshell, we can do proofs and give definitions by induction on the structure of unions and negations-and, thus, intersections-but arrows, products, and basic types are the base cases for the induction.

[Const] Γ ⊢ 𝑐 : b 𝑐 [Ax] Γ ⊢ 𝑥 : Γ(𝑥) [→I] Γ, 𝑥 : u ⊢ 𝑒 : 𝑡 Γ ⊢ 𝜆𝑥 .𝑒 : u → 𝑡 [→E] Γ ⊢ 𝑒 1 : 𝑡 1 → 𝑡 2 Γ ⊢ 𝑒 2 : 𝑡 1 Γ ⊢ 𝑒 1 𝑒 2 : 𝑡 2 [×I] Γ ⊢ 𝑒 1 : 𝑡 1 Γ ⊢ 𝑒 2 : 𝑡 2 Γ ⊢ (𝑒 1 , 𝑒 2 ) : 𝑡 1 × 𝑡 2 [×E 1 ] Γ ⊢ 𝑒 : 𝑡 1 × 𝑡 2 Γ ⊢ 𝜋 1 𝑒 : 𝑡 1 [×E 2 ] Γ ⊢ 𝑒 : 𝑡 1 × 𝑡 2 Γ ⊢ 𝜋 2 𝑒 : 𝑡 2 [0] Γ ⊢ 𝑒 : 0 Γ ⊢ (𝑒∈𝜏) ? 𝑒 1 : 𝑒 2 : 0 [∈ 1 ] Γ ⊢ 𝑒 : 𝜏 Γ ⊢ 𝑒 1 : 𝑡 1 Γ ⊢ (𝑒∈𝜏) ? 𝑒 1 : 𝑒 2 : 𝑡 1 [∈ 2 ] Γ ⊢ 𝑒 : ¬𝜏 Γ ⊢ 𝑒 2 : 𝑡 2 Γ ⊢ (𝑒∈𝜏) ? 𝑒 1 : 𝑒 2 : 𝑡 2 [∨] Γ ⊢ 𝑒 ′ : 𝑠 Γ, 𝑥 : 𝑠 ∧ u ⊢ 𝑒 : 𝑡 Γ, 𝑥 : 𝑠 ∧ ¬u ⊢ 𝑒 : 𝑡 Γ ⊢ 𝑒{𝑒 ′ /𝑥 } : 𝑡 [∧] Γ ⊢ 𝑒 : 𝑡 1 Γ ⊢ 𝑒 : 𝑡 2 Γ ⊢ 𝑒 : 𝑡 1 ∧ 𝑡 2 [Inst] Γ ⊢ 𝑒 : 𝑡 Γ ⊢ 𝑒 : 𝑡𝜎 [≤] Γ ⊢ 𝑒 : 𝑡 Γ ⊢ 𝑒 : 𝑡 ′ 𝑡 ≤ 𝑡 ′with these additional rules for the extensions of Appendix A (let-bindings and type constraints):[Let]Γ ⊢ 𝑒 1 : 𝑡 1 Γ ⊢ 𝑒 2 : 𝑡 2

map(𝑋 ↦ → 𝑓 (𝑋 ), Subst(Ψ, H 1 , H 2 )) = def Subst(Ψ, 𝑓 (H 1 ), 𝑓 (H 2 )) map(𝑋 ↦ → 𝑓 (𝑋 ), Var (x, H 1 , H 2 )) = def Var (x, 𝑓 (H 1 ), 𝑓 (H 2 )) [FormVar] x ∉ dom(Γ) Γ ⊢ R ⟨x | infer⟩ ⇒ Var (x, infer, untyp) [FormOk] Γ ⊢ R ⟨x | infer⟩ ⇒ Ok(typ) [BindInfer] Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-skip (infer)⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | infer⟩ ⇒ R [BindTrySkip 1 ] Γ ⊢ * R ⟨𝜅 | K⟩ ⇒ Var (x, K 1 , K 2 ) Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-keep (infer, K 1 , K 2 )⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-skip (K)⟩ ⇒ R [BindTrySkip 2 ] Γ ⊢ * R ⟨𝜅 | K⟩ ⇒ Ok(K ′ ) Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-skip (K)⟩ ⇒ Ok(skip (K ′ )) [BindTrySkip 3 ] Γ ⊢ * R ⟨𝜅 | K⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-skip (K)⟩ ⇒ map(𝑋 ↦ → try-skip (𝑋 ), R) [BindSkip 1 ] Γ ⊢ * R ⟨𝜅 | K⟩ ⇒ Var (x, K 1 , K 2 ) Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | skip (K 2 )⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | skip (K)⟩ ⇒ R [BindSkip 2 ] Γ ⊢ * R ⟨𝜅 | K⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | skip (K)⟩ ⇒ map(𝑋 ↦ → skip (𝑋 ), R) 40:47 [BindTryKeep 1 ] Γ ⊢ * R ⟨𝑎 | A⟩ ⇒ Ok(A ′ ) Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | keep (A ′ , {(1, K 1 )}, ∅)⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-keep (A, K 1 , K 2 )⟩ ⇒ R [BindTryKeep 2 ] Γ ⊢ * R ⟨𝑎 | A⟩ ⇒ Fail Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | skip (K 2 )⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-keep (A, K 1 , K 2 )⟩ ⇒ R [BindTryKeep 3 ] Γ ⊢ * R ⟨𝑎 | A⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | try-keep (A, K 1 , K 2 )⟩ ⇒ R ′ where R ′ = map(𝑋 ↦ → try-keep (𝑋, K 1 , K 2 ), R). [BindOk] Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | keep (A, ∅, S)⟩ ⇒ Ok(keep (A, ∅, S)) [BindKeep 1 ] Γ ⊢ P ⟨𝑎 | A⟩ ⇒ a Γ ⊢ A [𝑎 | a] : 𝑠 Γ, x : 𝑠 ∧ u ⊢ * R ⟨𝜅 | K⟩ ⇒ Ok(K ′ ) Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | keep (A, S, {(u, K ′ )} ∪ S ′ )⟩ ⇒ R Γ ⊢ R ⟨bind x = 𝑎 in 𝜅 | keep (A, {(u, K)} ∪ S, S ′ )⟩ ⇒ R [BindKeep 2 ]

x 1 ∈ dom(Γ)[Case 2 ] 𝜎 ∈ tally({Γ(x) ≤ ¬𝜏 }) Γ ⊢ P ⟨(x∈𝜏) ? x 1 : x 2 | typ⟩ ⇒ ∈ 2 ({𝜎 }) x 2 ∈ dom(Γ) [Lambda] Γ, 𝑥 : u ⊢ P ⟨𝜅 | K⟩ ⇒ k Γ ⊢ P ⟨𝜆𝑥 .𝜅 | 𝜆(u, K)⟩ ⇒ 𝜆(u, k) [Var] 𝜌 = refresh(Γ(x)) Γ ⊢ P ⟨x | typ⟩ : 𝜌 [BindSkip] Γ ⊢ P ⟨𝜅 | K⟩ ⇒ k Γ ⊢ P ⟨bind x = 𝑎 in 𝜅 | skip (K)⟩ ⇒ skip k x ∉ dom(Γ) [BindKeep]

https://en.wikipedia.org/wiki/Hydra_game

ACKNOWLEDGMENTS

We warmly thank the POPL reviewers: their careful reading and suggestions allowed us to improve the presentation significantly. A special thank the reviewers of the POPL artifact evaluation for their detailed and insightful reviews.

This work was partially supported by the Chaire Langages Dynamiques pour les Données of the Fondation Université Paris-Saclay, by the SECUREVAL ANR project n. ANR-22-PECY-0005 and by a CIFRE PhD. grant with Remote Technology.

Code 1 features the examples used in the introduction. Code 2 implements Curry's fix-point combinator in a call-by-value setting. Though it is traditionally given the type ((𝛽 → 𝛼) → (𝛽 → 𝛼)) → (𝛽 → 𝛼), our prototype infers a slightly more precise type by intersecting the co-domain of the argument with 𝛾.

Code 3 shows how to use the fix-point combinator to type recursive functions. The map_stub function implements a step of the traditional map function. The type inferred for this function has been omitted for simplicity. Then, map is obtained by applying the fixed-point combinator to map_stub. Note that [𝛼 * ] denotes a list of elements of type 𝛼, and [] denotes an empty list. The branch 1 → [] → [] may be surprising, but it is correct since the map function does not use its first argument if the second argument is an empty list.

Code 4 shows how type annotations (cf. Appendix A.2) can be used to infer more precise types: when the filter function is applied to a characteristic function for the set 𝛼 ∨𝛽 whose type precises

□ Now, we introduce a new order ≤ P on types. Intuitively, it adds to the subtyping order ≤ the possibility to instantiate polymorphic type variables. We then use it in the statement of a monotonicity lemma that will be used extensively in the next sections. Definition I.2 (Polymorphic subtyping order). We define the order relation ≤ P over types as follows:

Note that, while this order will be extensively used in the proofs, it will not be used in algorithms as we have no way to compute it. Indeed, while deciding this order might seem equivalent to solving a tallying problem (defined in Section 4.1), it is actually not the case as deciding this order requires to find a set of substitutions Σ, and not a single substitution 𝜎.

Definition I.3. For any order relation ≤ over types, we define the order relation ≤ over environments as follows:

∀Γ 1 , Γ 2 . Γ 1 ≤ Γ 2 ⇔ ∀ 𝑥 /x ∈ dom(Γ 2 ). 𝑥 /x ∈ dom(Γ 1 ) and Γ 1 (𝑥) ≤ Γ 2 (𝑥)

• 𝑒 ′ is not a binding variable (no aliasing) Definition I.8 (Binding context). A binding context 𝐵 is an ordered list of mappings from binding variables to atoms. Each mapping is written as a pair (x, 𝑒). We note these lists extensionally by separating elements by a semicolon, that is, (x 1 , 𝑎 1 ); . . . ; (x 𝑛 , 𝑎 𝑛 ) and use 𝜀 to denote the empty list.

We note 𝑒𝐵 the expression 𝑒{𝑒 𝑛 /x 𝑛 } . . . {𝑒 1 /x 1 } where (x 1 , 𝑒 1 ); . . . ; (x 𝑛 , 𝑒 𝑛 ) are the pairs in 𝐵.

Given a derivation 𝐷 and a node 𝑁 of 𝐷, we call binding context of 𝑁 in 𝐷 the binding context (x 1 , 𝑒 1 ); . . . ; (x 𝑛 , 𝑒 𝑛 ), where {𝑒 1 /x 1 }; . . . ; {𝑒 𝑛 /x 𝑛 } are the substitutions made by all the [∨] nodes crossed by one of their body premises when going from the root of 𝐷 to 𝑁 .

In the following definition, we use an order over expressions: Definition I.9 (Expression order). A (possibly partial) order ≤ is called expression order if it is an order over expressions modulo 𝛼-renaming, and if it is an extension of the sub-expression order: if 𝑒 1 is a sub-expression of 𝑒 2 modulo 𝛼-renaming, then we should have 𝑒 1 ≤ 𝑒 2 . We write 𝑒 1 < 𝑒 2 when 𝑒 1 ≤ 𝑒 2 and 𝑒 2 ̸ ≤ 𝑒 1 .

Definition I.10 (Well-positionned [∨] node). A [∨] node 𝑁 of a derivation 𝐷, of binding context 𝐵 in 𝐷 and doing the substitution 𝑒{𝑒 1 /x}, is said well-positionned in 𝐷 relatively to the expression order ≤ (or more succintly, well-positionned in (𝐷, ≤)) if there is no node 𝑁 ′ on the path from the root to 𝑁 such that:

• Every variable in fv(𝑒 1 𝐵) is in the type environment of 𝑁 ′ , and

rule of binding context 𝐵 ′ and performing a substitution {𝑒 2 /y} such that 𝑒 1 𝐵 < 𝑒 2 𝐵 ′ .

Definition I.11 ([∨]-canonical derivation). For a given expression order ≤, a derivation 𝐷 is [∨]-canonical for the order ≤ if every [∨] node it contains is acceptable and well-positionned in (𝐷, ≤). Definition I.12 (Form derivations, atomic derivations). A derivation 𝐷 is a form derivation if every segment of branch containing the root and stopping at the first [∨] node (if any, or stopping at a leaf otherwise):

• does not contain any structural node, and • if it ends with a [∨] node 𝑁 , the definition premise of 𝑁 is an atomic derivation, and the body premises of 𝑁 are form derivations. A derivation 𝐷 is an atomic derivation if every segment of branch containing the root and stopping at the first [→I] node (if any, or stopping at a leaf otherwise):

• does not contain any [∨] node, and • contains exactly one structural node, and • if it ends with a [→I] node 𝑁 , the premise of 𝑁 is a form derivation.

A derivation that is both a [∨]-canonical derivation and a form derivation is called [∨]-canonical form derivation.

Lemma I.13 (Normalisation of [∨]

). Given an expression order ≤, any ground derivation 𝐷 of Γ ⊢ : 𝑒 : 𝑡 can be transformed into a [∨]-canonical form derivation of Γ ⊢ : 𝑒 : 𝑡 ′ for the order ≤ and with 𝑡 ′ ≤ P 𝑡.

Proof. First, we can remove any aliasing (i.e. [∨] nodes doing a substitution {y/x}) by applying Lemma I.6 as needed. We can also trivially remove useless [∨] nodes (i.e. those doing a substitution 𝑒{𝑒 x /x} where 𝑒 does not contain x).

Then, let's consider, in the whole derivation, all the nodes that satisfy one of those conditions: If there is no such node, then the properties of Lemma I.13 are satisfied. Otherwise, we associate to each of these faulty nodes an expression and a binding context:

• For a structural node applied on an expression 𝑒 in a binding context 𝐵, we associate (𝑒, 𝐵),

• For a [∨] node doing the substitution {𝑒 x /x} in a binding context 𝐵, we associate (𝑒 x , 𝐵).

Now, we select among those nodes the one whose associated pair is minimal with respects to the following order:

Let's call this node 𝑁 , and its associated expression and binding context 𝑒 and 𝐵 respectively. Now, let's locate, in the segment from the root to 𝑁 , the farthest location 𝐿 from the root such that 𝑁 would be well-positionned in (𝐷, ≤) at this location.

Let's note Γ 𝐿 ⊢ : 𝑒 𝐿 : 𝑡 𝐿 the judgement at this location. We consider the subderivation 𝐷 𝐿 that derives this judgement in 𝐷. Note that, in 𝐷 𝐿 , there is no [∨] node that makes a substitution {𝑒 y /y} with 𝑒 y a strict sub-expression of 𝑒, because 𝑒 y 𝐵 y (with 𝐵 y the binding context of the corresponding [∨] node) would be smaller than 𝑒𝐵 for ≤, thus making it not well-positionned and contradicting the minimality of (𝑒, 𝐵). Also note that 𝐷 𝐿 contains the node 𝑁 (otherwise, the [∨] node 𝑁 would not be well-positionned at location 𝐿).

We apply Lemma I.5 on the root of 𝐷 𝐿 so that it performs the substitution 𝑒 ′ 𝐿 {𝑒/z} using the decomposition {u 𝑖 } 𝑖 ∈𝐼 = {1}, with z fresh and 𝑒 ′ 𝐿 an expression that does not contain 𝑒 and such that 𝑒 ′ 𝐿 {𝑒/z} ≡ 𝑒 𝐿 {𝑒/z} (basically, 𝑒 ′ 𝐿 is 𝑒 𝐿 where occurrences of 𝑒 have been replaced by z). This lemma can be applied as:

• There cannot be in our subderivation any [∨] node substituting a strict sub-expression of 𝑒, 𝐿 by using Lemma I.6, as well as other aliasing that would have been introduced in other branches. Not that, if 𝑁 is a structural node, it has already been eliminated by the application of Lemma I.5. Finally, we replace the subderivation 𝐷 𝐿 by 𝐷 ′ 𝐿 in 𝐷.

We conclude by repeating this whole process until all the nodes satisfy the conditions. We are guaranteed that it terminates by the fact that 𝑒𝐵 strictly increases for ≤ at each iteration (with (𝑒, 𝐵) the pair associated to the choosen node 𝑁 ). □

Normalisation of [Inst] nodes

Lemma I. Top-level reductions:

Parallel reductions:

In particular, notice that expression substitutions are up to 𝛼-renaming and perform only one pass.

Here is an example of reduction step using the parallel semantics: Proof. We proceed by structural induction on 𝐷.

If the root is not a [∨] node, or if it is an unavoidable [∨] node, then we proceed inductively on all its premises.

Otherwise, if the root is an avoidable [∨] node, we know that it is doing a substitution 𝑒{𝑣/x} with 𝑣 a value (as 𝐷 is canonical, the [∨] node must be acceptable). We can thus apply Lemma I.26 on its definition premise Γ ⊢ : 𝑣 : 𝑠: we deduce that 𝑠 is atomic. Consequently, among the types {u 𝑖 } 𝑖 ∈𝐼 composing the partition of 1 used by this node, there is a type u 𝑖 , for some 𝑖 ∈ 𝐼 , such that 𝑠 ≤ u 𝑖 .

We consider the corresponding body premise Γ, x : 𝑠 ⊢ : 𝑒 : 𝑡, and we proceed by induction on it in order to get a minimal derivation 𝐷 ′ for Γ, x : 𝑠 ⊢ : 𝑒 : 𝑡. Similarly, we proceed by induction on the definition premise Γ ⊢ : 𝑣 : 𝑠 in order to get a minimal derivation 𝐴 for Γ ⊢ : 𝑣 : 𝑠. We can then derive Γ ⊢ : 𝑒{𝑣/x} : 𝑡 from 𝐷 ′ by replacing [Ax ∨ ] nodes applying on x by the derivation 𝐴 (in a similar way as done in Lemma I.24, but for a binding variable). This new derivation is minimal.

We proceed by structural induction on 𝐷. If 𝑒 contains no occurrence of 𝑒 • (modulo 𝛼-renaming), this result is trivial. Thus, we will suppose in the following that 𝑒 contains at least one occurrence of 𝑒 • .

We denote by 𝑒 ′ the expression 𝑒{𝑒 ′ • /𝑒 • }, and consider the root of the derivation: [Const] Impossible case (𝑒 cannot contain any reducible expression).

[Ax 𝜆 ] Impossible case (𝑒 cannot contain any reducible expression).

[Ax ∨ ] Impossible case (𝑒 cannot contain any reducible expression).

[≤] By induction on the premise Γ ⊢ 𝑒 : 𝑡 ′ (with 𝑡 ′ ≤ 𝑡), we get a derivation for Γ ⊢ 𝑒 ′ : 𝑡 ′ , thus we can derive Γ ⊢ 𝑒 ′ : 𝑡 by using a [≤] node.

[Inst] Similar to the previous case.

[∧] We proceed inductively on each premise, and intersect the resulting derivations with a [∧]

node.

[→I] We have 𝑒 ′ ≡ 𝜆𝑥 . (𝑒 𝜆 {𝑒 ′

• /𝑒 • }) for some expression 𝑒 𝜆 . We can derive Γ, 𝑥 : u ⊢ : 𝑒 𝜆 {𝑒 ′ • /𝑒 • } : 𝑡 ′ by induction on the premise Γ, 𝑥 : u ⊢ : 𝑒 𝜆 : 𝑡 ′ (with 𝑡 ≃ u → 𝑡 ′ ) and conclude by using a [→I] node.

[×I] We have

for some 𝑒 1 and 𝑒 2 . We proceed inductively on the premises, as in the previous case.

[→E] We have 𝑒 ≡ 𝑒 1 𝑒 2 for some expressions 𝑒 1 and 𝑒 2 . If 𝑒 • is a sub-expression of 𝑒 1 and/or 𝑒 2 , we conclude by proceeding inductively on the premises, as in the previous case.

Otherwise, 𝑒 • ≡ 𝑒 1 𝑒 2 and thus the reduction 𝑒 • ⊤ 𝑒 ′ • uses the rule 7. Consequently, we know that 𝑒 • ≡ 𝑒 ≡ (𝜆𝑥 . 𝑒 𝜆 )𝑣 for some expression 𝑒 𝜆 and value 𝑣, and 𝑒 ′

• ≡ 𝑒 ′ ≡ 𝑒 𝜆 {𝑣/𝑥 }. We have the following premises:

(1) Γ ⊢ : 𝜆𝑥 . 𝑒 𝜆 :

: 𝑣 : 𝑡 1 As 𝐷 is minimal, we know that the premise (1) has no [∨] node, except possibly in the premise of a [→I] node. Thus, we can extract from (1) a collection of derivations of the judgements Γ, 𝑥 : u 𝑖 ⊢ : 𝑒 𝜆 : 𝑠 𝑖 for 𝑖 ∈ 𝐼 , such that there exists some instantiations

We know that 𝐽 is not empty: the case 𝑡 1 ≤ 𝑖 ∈𝐼 u 𝑖 ≃ 0 is impossible since the value 𝑣 would have type 0, contradicting Proposition I.23. For every 𝑗 ∈ 𝐽 , we define 𝐼 𝑗 = {𝑖 ∈ 𝐼 | v 𝑖 ∧ u 𝑖 0} (𝐼 𝑗 cannot be empty as the partition {v 𝑗 } 𝑗 ∈𝐽 has minimal cardinality). Note that for any 𝑗 ∈ 𝐽 and 𝑖 ∈ 𝐼 𝑗 , we have v 𝑗 ≤ u 𝑖 . Using the monotonicity lemma (Lemma I.4), we can derive for every 𝑗 ∈ 𝐽 and 𝑖 ∈ 𝐼 𝑗 the judgement Γ, 𝑥 : v 𝑗 ⊢ : 𝑒 𝜆 : 𝑠 𝑖 Σ 𝑖 . Thus, using a [∧] node, for every 𝑗 ∈ 𝐽 we can derive Γ, 𝑥 : v 𝑗 ⊢

: (1) Definition premise: Γ ⊢ : 𝑒 2 : 𝑠 (2) Body premises: ∀𝑖 ∈ 𝐼 . Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 1 : 𝑡 As 𝐷 is minimal, we know that 𝑒 2 cannot be a value. Also, we know that 𝑒 • does not contain x as a free variable (otherwise there would be no occurrence of 𝑒 • in 𝑒 1 {𝑒 2 /x}). Consequently, 𝑒 ′

• does not contain x neither, because a reduction step cannot introduce a new free variable.

There are several cases:

• 𝑒 • does not contain 𝑒 2 and 𝑒 2 does not contain 𝑒 • . In this case, we have:

. Thus, we can easily conclude by keeping the definition premise of the [∨] node and applying the induction hypothesis on the body premises.

• 𝑒 2 contains 𝑒 • . In this case, we pose

As 𝑒 2 is not a value, it can only appear in 𝑒 • inside of a 𝜆-abstraction, and/or inside of a branch of a typecase: otherwise, 𝑒 • would not be reducible. Thus, we can deduce that 𝑒

Consequently, we can easily conclude by keeping the definition premise of the [∨] node and applying the induction hypothesis on the body premises.

[0] We have 𝑒 ≡ (𝑒 1 ∈𝜏) ? 𝑒 2 : 𝑒 3 for some 𝑒 1 , 𝑒 2 and 𝑒 3 . As values cannot have the type 0 (Proposition I.23), we know that 𝑒 1 is not a value. Thus,

We can derive Γ ⊢

: 𝑒 1 {𝑒 ′ • /𝑒 • } : 0 by proceeding inductively on the premise, and then we can derive Γ ⊢ : 𝑒 ′ : 0 by using [0]. [∈ 1 ] We have 𝑒 ≡ (𝑒 1 ∈𝜏) ? 𝑒 2 : 𝑒 3 for some 𝑒 1 , 𝑒 2 and 𝑒 3 . There are three cases: Proof. We proceed by structural induction on 𝐷. We consider the root of the derivation:

[Const] Trivial (𝑒 is a value).

[Ax 𝜆 ] Impossible case (contradict the hypotheses).

[Ax ∨ ] Impossible case (contradict the hypotheses).

[≤] Trivial (by induction on the premise).

[Inst] Trivial (by induction on the premise).

[∧] Trivial (by induction on one of the premises).

[→I] Trivial (𝑒 is a value).

[×I] We have 𝑒 ≡ (𝑒 1 , 𝑒 2 ) for some expressions 𝑒 1 and 𝑒 2 .

• If 𝑒 1 is not a value, and as we have ∀𝐸, 𝑥 /x. 𝑒 1 𝐸 [ 𝑥 /x], we know by applying the induction hypothesis that 𝑒 1 can be reduced. Thus, 𝑒 can also be reduced under the evaluation context ( [ ], 𝑒 2 ).

• If 𝑒 1 is a value, then we can apply the induction hypothesis on the second premise (as 𝑒 1 is a value, we know that ∀𝐸, 𝑥 /x. 𝑒 2 𝐸 [ 𝑥 /x]). It gives that either 𝑒 2 is a value or it can be reduced. We can easily conclude in both cases: if 𝑒 2 is a value, then 𝑒 is also a value, otherwise, 𝑒 can be reduced under the evaluation context (𝑒 1 , [ ]).

[→E] We have 𝑒 ≡ 𝑒 1 𝑒 2 for some expressions 𝑒 1 and 𝑒 2 , with Γ ⊢ : 𝑒 1 : 𝑠 → 𝑡 and Γ ⊢ : 𝑒 2 : 𝑠. • If 𝑒 1 is not a value, and as we have ∀𝐸, 𝑥 /x. 𝑒 1 𝐸 [ 𝑥 /x], we know by applying the induction hypothesis that 𝑒 1 can be reduced. Thus, 𝑒 can also be reduced under the evaluation context [ ]𝑒 2 .

• If 𝑒 1 is a value, we can apply Proposition I.23 on it: as Γ ⊢

: 𝑒 1 : 0 → 1, it implies that 𝑒 1 ∈ 0 → 1 and thus 𝑒 1 ≡ 𝜆𝑥 . 𝑒 𝜆 for some 𝑒 𝜆 . Moreover, we can apply the induction hypothesis on the second premise (as 𝑒 1 is a value, we know that ∀𝐸, 𝑥 /x. 𝑒 2 𝐸 [ 𝑥 /x]). It gives that either 𝑒 2 is a value or it can be reduced. We can easily conclude in both cases: if 𝑒 2 is a value, then 𝑒 can be reduced using the rule 7, otherwise, 𝑒 can be reduced under the evaluation context 𝑒 1 [ ].

[×E 1 ] We have 𝑒 ≡ 𝜋 1 𝑒 1 for some 𝑒 1 , with Γ ⊢ : 𝑒 1 : 𝑡 × 𝑠. By applying the induction hypothesis on the premise, we know that 𝑒 1 is either a value or it can be reduced. If 𝑒 1 can be reduced, then 𝑒 can also be reduced under the evaluation context 𝜋 1 [ ]. Otherwise, as Γ ⊢ : 𝑒 1 : 1 × 1, we can apply Proposition I.23 on it, yielding 𝑒 1 ∈ 1 × 1. Thus, 𝑒 1 ≡ (𝑣 1 , 𝑣 2 ) for some values 𝑣 1 and 𝑣 2 , and consequently 𝑒 can be reduced using the rule 8.

[×E 2 ] Similar to the previous case.

[∨] We have 𝑒 ≡ 𝑒 1 {𝑒 2 /x} for some 𝑒 1 and 𝑒 2 , with Γ ⊢ : 𝑒 2 : 𝑠 and ∀𝑖 ∈ 𝐼 . Γ, x : 𝑠 ∧ u 𝑖 ⊢ : 𝑒 1 : 𝑡. There are two cases:

• There exists an evaluation context 𝐸 such that 𝑒 1 ≡ 𝐸 [x]. In this case, we know that ∀𝐸 ′ , 𝑦 /y. 𝑒 2 𝐸 ′ [ 𝑦 /y], otherwise we would have 𝑒 ≡ 𝐸 [𝐸 ′ [ 𝑦 /y]]. Thus, we can apply the induction hypothesis on the definition premise. It gives that either 𝑒 2 is a value or it can be reduced. As 𝐷 is minimal, 𝑒 2 cannot be a value and thus 𝑒 2 can be reduced. Consequently, 𝑒 can also be reduced under the evaluation context 𝐸.

• There is no evaluation context 𝐸 such that 𝑒 1 ≡ 𝐸 [x]. In this case, we apply the induction hypothesis on one of the body premises. It gives that either 𝑒 1 is a value or it can be reduced. We can easily conclude in both cases: if 𝑒 1 is a value, then 𝑒 is also a value, otherwise, 𝑒 can be reduced.

[0] We have 𝑒 ≡ (𝑒 1 ∈𝜏) ? 𝑒 2 : 𝑒 3 for some 𝑒 1 , 𝑒 2 and 𝑒 3 , with Γ ⊢ : 𝑒 1 : 0. As values cannot have the type 0 (Proposition I.23), we know that 𝑒 1 is not a value. Thus, by applying the induction hypothesis on the premise, we know that 𝑒 1 can be reduced. Consequently, 𝑒 can be reduced under the evaluation context ([ ]∈𝜏) ? 𝑒 2 : 𝑒 3 .

[∈ 1 ] We have 𝑒 ≡ (𝑒 1 ∈𝜏) ? 𝑒 2 : 𝑒 3 for some 𝑒 1 , 𝑒 2 and 𝑒 3 , with Γ ⊢ : 𝑒 1 : 𝜏. We thus have 𝑒 1 ∈ 𝜏 (Proposition I.23). By applying the induction hypothesis on the first premise, we know that 𝑒 1 is either a value or it can be reduced. If 𝑒 1 is a value, then 𝑒 can be reduced using the rule 10. Otherwise, 𝑒 1 can be reduced and thus 𝑒 can also be reduced under the evaluation context ([ ]∈𝜏) ? 𝑒 2 : 𝑒 3 .

[∈ 2 ] Similar to the previous case. Proof. We procced by structural induction on the derivation 𝐷 (with 𝐷 being a canonical form derivation).

□

If the root of 𝐷 is a [Ax ∨ ] node, we can directly conclude by choosing Σ ′ = Σ.

Otherwise, if the root 𝐷 is a [∨] node, doing a substitution 𝑒{𝑒 ′ /x}, we first proceed inductively on its body premises (which all are canonical form derivations). It yields some derivations ΓΣ ′ 𝑖 , x : 𝑠Σ ′ 𝑖 ∧ u 𝑖 ⊢ : 𝑒 : 𝑡 Σ for 𝑖 ∈ 𝐼 . We consider the set of substitutions Σ ′ = 𝑖 ∈𝐼 Σ ′ 𝑖 . Now, let's try to derive ΓΣ ′′ ⊢ : 𝑒 ′ : 𝑠 ′ , for some Σ ′′ and 𝑠 ′ , with 𝑠 ′ ≤ 𝑠Σ ′ (≤ 𝑠Σ ′ 𝑖 for every 𝑖 ∈ 𝐼 ). For that, we consider the definition premise of 𝐷, deriving Γ ⊢ : 𝑒 ′ : 𝑠, and apply the following process on it:

• If its root is a [∧] node, we apply inductively this process to each premise Γ ⊢ : 𝑒 ′ : 𝑠 𝑗 (with 𝑗 ∈𝐽 𝑠 𝑗 ≃ 𝑠) in order to derive some ΓΣ ′′ 𝑗 ⊢ : 𝑒 ′ : 𝑠 ′ 𝑗 , with 𝑠 ′ 𝑗 ≤ 𝑠Σ ′ . We then consider Σ ′′ = 𝑗 ∈𝐽 Σ ′′ 𝑗 , and derive for each 𝑗 ∈ 𝐽 ΓΣ ′′ ⊢ : 𝑒 ′ : 𝑠 ′ 𝑗 using Lemma I.34. Finally, we intersect the those derivations with a [∧] node.

• If its root is a [→E] node, we apply the following transformation:

• If its root is a [→I] node, we apply the following transformation:

with 𝐴 ′ obtained by applying the induction hypothesis on 𝐴 and Σ ′ .

• If its root is a [Ax 𝜆 ] node, we apply the following transformation:

• The other cases are similar. We thus have Σ ′′ and 𝑠 ′ such that ΓΣ ′′ ⊢ : 𝑒 ′ : 𝑠 ′ and ∀𝑖 ∈ 𝐼 . 𝑠 ′ ≤ 𝑠Σ ′ 𝑖 . We transform, for each 𝑖 ∈ 𝐼 , the derivation ΓΣ Proof. Straightforward induction on the derivation of Γ ⊢ : 𝑒 : 𝑡. The type substitution 𝜓 can be applied to every type (and type environment) in the derivation, and as dom(𝜓 ) ⊆ V 𝑀 it will not conflict with any [Inst] node in the derivation. Subtyping relations are preserved as

Lemma I.37 (Generalisation lemma). If Γ, 𝑥 : 𝑠𝜙 ⊢ : 𝑒 : 𝑡 with 𝑒 a ground expression, 𝜙#Γ, and Γ ⊢ : 𝑒 ′ : 𝑠, then Γ ⊢ : 𝑒{𝑒 ′ /𝑥 } : 𝑡 ′ for some 𝑡 ′ such that there exists a substitution 𝜙 ′ such that 𝑡 ′ 𝜙 ′ ≃ 𝑡 and 𝜙 ′ #Γ.

Proof. We can suppose, without loss of generality, that 𝜙 is a full generalisation, that is, an injective substitution mapping every type variable in (vars(𝑥) ∧ V 𝑀 ) ∖ vars(Γ) to a fresh polymorphic type variable (and being the identity for any other type variable). Indeed, if for any 𝜙 the conditions of Lemma I.37 are satisfied, then they are also satisfied for any such generalisation according to the monotonicity lemma (Lemma I.4). Now, we transform the derivation of Γ, 𝑥 : 𝑠𝜙 ⊢ : 𝑒 : 𝑡 into a canonical form derivation 𝐷 of Γ, 𝑥 : 𝑠𝜙 ⊢ : 𝑒 : 𝑡 ′ (with 𝑡 ′ ≤ P 𝑡) using Theorem I.22. Let Σ such that 𝑡 ′ Σ ≤ 𝑡. Then, using Lemma I.35, we transform 𝐷 into a derivation 𝐷 ′ of ΓΣ ′ , 𝑥 : (𝑠𝜙)Σ ′ ⊢ : 𝑒 : 𝑡 ′ Σ, for some Σ ′ , and such that 𝐷 ′ does not contain any [Inst] node. Adding a [≤] at the root gives ΓΣ ′ , 𝑥 : (𝑠𝜙)Σ ′ ⊢ : 𝑒 : 𝑡.

40:69

Let 𝜎 ′′ be an injective substitution mapping every polymorphic type variable appearing in the image of at least one of the substitutions 𝜎 ′ ∈ Σ ′ and 𝜙 to a fresh monomorphic type variable (and being the identity for any other type variable). From ΓΣ ′ , 𝑥 : (𝑠𝜙)Σ ′ ⊢ : 𝑒 : 𝑡, we can derive a derivation of (ΓΣ ′ )𝜎 ′′ , 𝑥 : ((𝑠𝜙)Σ ′ )𝜎 ′′ ⊢ : 𝑒 : 𝑡𝜎 ′′ simply by renaming the type variables everywhere according to 𝜎 ′′ . This is only possible because the derivation does not contain any [Inst] node (substituting a polymorphic type variable by a monomorphic one could invalidate [Inst] nodes). By monotonicity (Lemma I.4), we get Γ, 𝑥 : ((𝑠𝜙)Σ ′ )𝜎 ′′ ⊢ : 𝑒 : 𝑡𝜎 ′′ . Now, we consider the set of substitutions 𝑖 ∈𝐼 (𝑠𝜓 𝑖 )𝜎 𝑖 , we get a derivation for Γ ⊢ : 𝑒{𝑒 ′ /𝑥 } : 𝑡𝜎 ′′ . We recall that the substitution 𝜎 ′′ is injective and thus inversible, with its inverse 𝜙 ′ being such that 𝜙 ′ #Γ (as the image of 𝜎 ′′ is only composed of fresh monomorphic type variables). Thus, we have (𝑡𝜎 ′′ )𝜙 ′ ≃ 𝑡, which concludes the proof. □

We now have all the results necessary to prove the type safety of programs for the parallel semantics. The parallel semantics is extended to programs in a straightforward way: I.2.5 Type Safety for the Source Semantics. The final step is to deduce a type safety theorem for the source semantics (Figure 1) from the type safety theorem for the parallel semantics. In order to help comparing the two reduction semantics, we introduce again another reduction semantics C on expressions that can perform a reduction ⊤ under any context C (not just an evaluation context):

. Lemma I.42. For any expressions 𝑒 1 and 𝑒 3 , if we have a chain 𝑒 1 I.3.2 Soundness. See Appendix G for the full algorithmic system, without the rules for extensions.

Lemma I.58 (Monotonicity). If

Proof. Straightforward structural induction on the derivation.

Proof. We proceed by structural induction on the typing derivation of

We consider the root of the derivation:

[→I-Alg] We have 𝑎 ≡ 𝜆𝑥 . 𝜅, and thus ⌈𝑎⌉ ≡ 𝜆𝑥 . ⌈𝜅⌉.

By induction on the premise, we get Γ, 𝑥 : u ⊢ : ⌈𝜅⌉ : 𝑠. By applying the rule [→I], we get Γ ⊢

:

With an [Ax ∨ ] node, we can derive Γ ⊢ : x 1 : Γ(x 1 ) and Γ ⊢ : x 2 : Γ(x 2 ). Using a [Inst∧≤] pattern, we can derive from that Γ ⊢ : x 1 : 𝑡 1 and Γ ⊢ : x 2 : 𝑡 2 . We have 𝑡 ≃ 𝑡 1 •𝑡 2 . Thus, according to the definition of •, we know that 𝑡 1 ≤ 𝑡 2 → 𝑡. Thus, with an application of the [≤] rule on Γ ⊢ : x 1 : 𝑡 1 , we can derive Γ ⊢ : x 1 : 𝑡 2 → 𝑡. We can then conclude with an application of the

With an [Ax ∨ ] node, we can derive Γ ⊢ : x 1 : Γ(x 1 )𝜌 1 and Γ ⊢ : x 2 : Γ(x 2 )𝜌 2 (with 𝜌 1 and 𝜌 2 as in the [×I-Alg] node). We can then conclude with an application of the [×I] rule.

[×E 1 -Alg] We have 𝑎 ≡ 𝜋 1 x. We pose 𝑡 • = Γ(x)Σ.

With an [Ax ∨ ] node, we can derive Γ ⊢ : x : Γ(x). Using a [Inst∧≤] pattern, we can derive from that Γ ⊢ : x : 𝑡 • . We have 𝑡 ≃ 𝝅 1 (𝑡 • ). Thus, according to the definition of 𝝅 1 , we know that 𝑡 • ≤ 𝑡 × 1. Thus, with an application of the [≤] rule, we can derive Γ ⊢ : x : 𝑡 × 1. We can then conclude with an application of the [×E 1 ] rule.

[×E 2 -Alg] Similar to the previous case.

[0-Alg] Similar to the previous case.

[∈ 1 -Alg] Similar to the previous case.

[∈ 2 -Alg] Similar to the previous case.

By induction on the premise, we get Γ ⊢ : ⌈𝜅 • ⌉ : 𝑡. As x ∉ dom(Γ), we know that this derivation does not contain any [Ax ∨ ] node applied on x. We can thus easily transform it into a derivation of Γ ⊢ : ⌈𝜅 • ⌉{⌈𝑎⌉/x} : 𝑡 by substituting every occurrence of x by ⌈𝑎⌉. Definition I.60 (Atomic source expression). We say that an expression of the source language is an atomic source expression if it can be constructed with the following syntax:

Atomic source expressions ā ::

and if, for the case 𝜆𝑥 .𝑒, all sub-expressions of 𝑒 are either a binding variable or they contain a lambda variable that is not in fv(𝜆𝑥 .𝑒).

The variable ā is used to range over atomic source expressions. Proof. Straightforward induction on 𝐵. □

In the following, we fix an expression order ≤ over expressions (c.f. Desfinition I.9). This order should be total, so that for any expression 𝑒, it determines a unique MSC-form MSC(𝑒) modulo 𝛼renaming (and not only modulo ≡ 𝜅 ): independent consecutive bindings must follow the increasing order ≤ of their unwinding. As the order ≤ is arbitrarily choosen, the proofs below will work for any MSC-form. Lemma I.65 (Decomposition of canonical form derivations). If 𝐷 is a canonical form derivation of Γ ⊢ : 𝑒 : 𝑡, with the root being a [∨] node doing the substitution 𝑒 ′ {𝑒 x /x}, then there exists a binding context 𝐵 such that MSC(𝑒) ≡ 𝛼 term(𝐵, bind x = MSCA(𝑒 x ∖ 𝐵) in MSC(𝑒 ′ ∖ 𝐵) ).

Proof. First, we deduce from the fact that 𝐷 is a canonical form derivation that the definition premise, Γ ⊢ : 𝑒 x : 𝑠, is an atomic derivation. We know that 𝑒 x appears in 𝑒 (as 𝐷 is canonical, see Definition I.7). Thus, we can deduce that MSC(𝑒) contains a definition for an atom 𝑎 that unwinds to 𝑒 x . Formally, we know that there exists a binding context 𝐵, an atom 𝑎 and a canonical form 𝜅 such that MSC(𝑒) ≡ 𝛼 term(𝐵, bind x = 𝑎 in 𝜅 ) with ⌈𝑎⌉𝐵 ≡ 𝛼 𝑒 x .

First, we determine what 𝐵 is. The expression 𝑒 x could contain some sub-expressions that are not binding variables and that have no occurrence of a lambda variable defined in 𝑒 x . In this case, these sub-expressions should be defined by some bindings in 𝐵 (the unwinding of each such binding is necessarily smaller than 𝑒 x by ≤, as ≤ is an extension of the sub-expressions order). The expression 𝑒 ′ could also contain some such sub-expressions. The ones whose unwnding is smaller than 𝑒 x according to ≤ must be defined by some bindings in 𝐵 too. No other expression should be defined in 𝐵 or it would contradict the properties of MSC-forms. Now, we determine what 𝑎 is. Under the context 𝐵, the expression 𝑒 x ∖ 𝐵 unwinds to 𝑒 x (Proposition I.64). Moreover, as Γ ⊢ : 𝑒 x : 𝑠 is an atomic derivation, and given how 𝐵 is constructed, 𝑒 x ∖ 𝐵 must be an atomic source expression. Thus, we can deduce from Proposition I.62 that MSCA(𝑒 x ∖ 𝐵) can be used in place of the atom 𝑎 without breaking any property of the MSC-form, and thus by unicity of the MSC-form (Proposition I.53) we can conclude that 𝑎 ≡ 𝛼 MSCA(𝑒 x ∖ 𝐵).

Finally, we determine what 𝜅 is. We note 𝐵 ′ the binding context 𝐵; (x, MSCA(𝑒 x ∖ 𝐵)). The expression 𝑒 ′ ∖ 𝐵 ′ unwinds to 𝑒 ′ {𝑒 x /x} under the context 𝐵 ′ (Proposition I.64). As 𝐷 is a canonical form, 𝑒 x cannot be a sub-expression of 𝑒 ′ (c.f. Definition I.7), thus 𝑒 ′ ∖ 𝐵 ≡ 𝛼 𝑒 ′ ∖ 𝐵 ′ and thus 𝑒 ′ ∖ 𝐵 also unwinds to 𝑒 ′ {𝑒 x /x}. Thus, MSC(𝑒 ′ ∖ 𝐵) can be used in place of 𝜅 without breaking any property of the MSC-form (note that it only contains top-level bindings for expressions whose unwinding is greater than 𝑒 x by ≤, as the smaller ones have been put in 𝐵). By unicity of the MSC-form (Proposition I.53), we conclude that 𝜅 ≡ 𝛼 MSC(𝑒 ′ ∖ 𝐵), and thus MSC(𝑒)

If 𝐷 is a canonical atomic derivation of Γ ⊢ : ā : 𝑡 (with ā an atomic source expression), then ∃a,

Proof. We proceed by induction on the depth of 𝐷. We consider the root of the derivation (the cases up to [∈ 2 ] are for canonical atomic derivations, the cases after are for canonical form derivations):

[Const] Trivial.

[Ax 𝜆 ] Trivial.

[→I] We have ā ≡ 𝜆x. 𝑒 and thus MSCA( ā) ≡ 𝛼 𝜆x. MSC(𝑒).

The premise of this [→I] node is a canonical form derivation. Thus, by induction on this premise, we get Γ, x : u ⊢ A [MSC(𝑒) | k] : 𝑡 ′ (with 𝑡 ′ ≤ P 𝑡). We can thus derive Γ ⊢ A [𝜆x. MSC(𝑒) | 𝜆(u, k)] : u → 𝑡 ′ and we have u → 𝑡 ′ ≤ P u → 𝑡, which concludes this case.

[→E] We have ā ≡ x 1 x 2 and thus MSCA( ā) ≡ 𝛼 x 1 x 2 .

As 𝐷 is a canonical atomic derivation, we know that the second premise, Γ ⊢ : x 2 : 𝑡 1 , is a [Inst∧≤] pattern with no [≤] node and whose premise is a [Ax ∨ ] node. Thus, we know that there exists Σ 2 such that Γ(x 2 )Σ 2 ≃ 𝑡 1 . Similarly, the first premise, Γ ⊢ : x 1 : 𝑡 1 → 𝑡 2 , is a [Inst∧≤] pattern whose premise is a [Ax ∨ ] node. Thus, we know that there exists Σ 1 such that Γ(x 1 )Σ 1 ≤ 𝑡 1 → 𝑡 2 . Consequently, and by definition of •, we know that

As 𝐷 is a canonical atomic derivation, both premises can only be [Ax ∨ ] nodes. Thus, we can deduce that there exists two renamings of polymorphic type variables 𝜌 1 and 𝜌 2 such that Γ(x 1 )𝜌 1 ≃ 𝑡 1 and Γ(x 2 )𝜌 2 ≃ 𝑡 2 . Thus, we can derive

As 𝐷 is a canonical atomic derivation, we know that the premise, Γ ⊢ : x : 𝑡 1 × 𝑡 2 , is a [Inst∧≤] pattern whose premise is a [Ax ∨ ] node. Thus, we know that there exists Σ such that Γ(x)Σ ≤ 𝑡 1 × 𝑡 2 . Consequently, and by definition of 𝝅 1 , we know that 𝝅 1 (Γ(x)Σ) ≤ 𝑡 1 . We can thus derive Γ ⊢ A [𝜋 1 x | 𝜋 (Σ)] : 𝑡 ′ (with 𝑡 ′ ≃ 𝝅 1 (Γ(x)Σ)) such that 𝑡 ′ ≤ 𝑡 1 , which concludes this case.

[×E 2 ] Similar to the previous case.

[0] We have ā ≡ (x∈𝜏) ? x 1 : x 2 and thus MSCA( ā) ≡ 𝛼 (x∈𝜏) ? x 1 : x 2 .

As 𝐷 is a canonical atomic derivation, we know that the premise, Γ ⊢ : x : 0, is a [Inst∧≤] pattern with no [≤] node and whose premise is a [Ax ∨ ] node. Thus, we know that there exists Σ such that Γ(x)Σ ≃ 0. We can thus derive Γ ⊢ A [(x∈𝜏) ? x 1 : x 2 | 0(Σ)] : 0.

[∈ 1 ] We have ā ≡ (x∈𝜏) ? x 1 : x 2 and thus MSCA( ā) ≡ 𝛼 (x∈𝜏) ?

As 𝐷 is a canonical atomic derivation, we know that the first premise, Γ ⊢ : x : 𝜏, is a [Inst∧≤] pattern whose premise is a [Ax ∨ ] node. Thus, we know that there exists Σ such that Γ(x)Σ ≤ 𝜏. Similarly, the second premise, Γ ⊢ : x 1 : 𝑡 1 , can only be a [Ax ∨ ]. Thus, we know that there exists a renaming of polymorphic variables 𝜌 such that Γ(x 1 )𝜌 ≃ 𝑡 1 . We can thus derive Γ ⊢ A [(x∈𝜏) ?

[≤] Straightforward induction on the premise.

[Inst] Straightforward induction on the premise.

[∧] By induction on the premises, we get ∀𝑖 ∈ 𝐼 . Proof. Direct application of Lemma I.66 after using the normalisation theorem (Theorem I.22) on a derivation of Γ ⊢ : 𝑒 : 𝑡. □

I.4 Annotations Reconstruction System

This section contains proofs for the reconstruction system (Appendix H). Proof. There can only be finitely many [Iterate 1 ] and [Iterate 2 ] nodes applied on a given canonical form or atom, otherwise, according to the previous lemmas, we could extract from them an infinite decreasing chain of ordinal numbers. □