• Aucun résultat trouvé

Typing Non-Functional Constructs

5.3 Our Approach in Id

5.3.2 Closing Open Structures

Another important concern is ecient implementation of functional data-structures. We gave examples of array comprehensions earlier. These generate functional arrays whose allocation and value is specied together. Clearly, the compiler will have to deal with them independently because of their more lenient typing mechanism. Each such construct adds to the syntactic complexity of the language and it would be nice if we can express them in terms of a small kernel language consisting of low level constructs. We will now examine some issues arising out of non-functional implementations of such functional constructs. In the following discussion, open objects refer to any kind of assignable data-structures and functions (with arbitrary, but nite

-nesting) that manipulate them.

In Id, array comprehensions are desugared into I-structure array allocations and loops that ll them according to the comprehension clauses. A similar strategy is adopted for list comprehensions9and other list manipulating functions (such asappendshown in section 5.1), all of which are implemented using open lists. The important point here is that, once constructed, these arrays and lists are guaranteed to be used only functionally. Therefore, we should be able to close the type of these open data-structures in order to regain the polymorphism lost due to their non-functional implementation. In Tofte's terminology, we wish to convert an imperative type variable into an applicative one whenever it is safe to do so.

The following example shows what we mean. The functionmake arrayreturns a functional array. We intend to implement it using a slight variation of ourmake i array function above.

def make array bounds f =

f (l,u) = bounds in

farray bounds

9List Comprehensions are similar to array comprehensions in structure and mechanism but generate lists instead. See [46, 48] for details.

| [i] = f i || i <- l to u gg;

read onlyis a special type coercion operator that has the eect of converting an I-structure array to a functional array. In particular, it accomplishes two important tasks. First, it actu-ally coerces the type(I array *0) to (Array *0). This enables the type-checker to identify subsequent assignments to that array as type-errors since only I-structure arrays are deemed assignable. Second, it lifts the polymorphic typing restrictions on the type variables of such structures rendering them truely functional. In other words, it substitutes applicative type variables for imperative ones in the types of these structures. Of course, this must be done with care so as not to generate unsound typings involving non-functional constructs, which we set out to correct in the rst place.

Both the above mentioned tasks are compile-time (specically, type-checking time) opera-tions and do not aect the compiled code. In the particular example shown above, the coercion is safe since we do not allow assignments to functional arrays and nobody has a handle on the I-structure alias of the array after the coercion is done. This last condition is our guarantee for generating only sound typings involving the closed array, because the array cannot be assigned to anymore. In compiler generated desugaring, we may be able to ensure that the I-structure array does not escape the block of code in which it is allocated and hence can be safely coerced to a functional array outside it, but in general, enforcing this condition may require a lot of compiler analysis.

5.3.3 Comments

Finally, we would like to point out that incremental typing of non-functional constructs using our incremental book-keeping mechanism described in chapter 3 is only slightly more complex.

In particular, we will have to maintain a few more compilation properties, such as which type variables are imperative, and what is their rank, etc. We even allow globally open

data-with rank 0. This is more general than the position taken in Standard ML of New Jersey, where top-level reference data-structures are forced to be grounded (no type variables allowed) as soon as they are allocated. A more detailed description of this scheme is beyond the scope of this thesis.

Conclusion

In this thesis, we described the mechanism of type inference in the programming language Id, as well as its other features that have a direct bearing upon the type system. We summarize our results below.

6.1 Summary

We used the Hindley/Milner type inference mechanism as a basis for the type system in Id. First, we described a deterministic set of type inference rules and the standard inference algorithm available in the literature that infers sound and complete types for complete programs. We also showed a translation of the Id Kernel language to the abstract mini-language used in the algorithm.

Then, we described our main research contribution, an

incremental

type inference mech-anism for Id. First, we established a general framework for incremental computation and maintenance of compile-time properties in the light of frequent additions or editing of the pro-gram. Then, we gave a detailed account of how to extend the Hindley/Milner type inference algorithm to work in this incremental framework and demonstrated a proof of its soundness and completeness with respect to the original type system. We evaluated the performance of this system and then discussed optimizations to improve upon it.

We also described other features of the language Id that are important from a type inference perspective. We presented a systematic scheme to permit overloading of identiers in the language, describing rules for their type inference and then discussing several mechanisms for their translation into actual code. We compared and evaluated two strategies in particular,

polymorphic non-functional constructs of Id. We outlined three schemes, each giving more exibility and polymorphism to non-functional constructs than the previous one, while still maintaining the soundness of type inference.