• Aucun résultat trouvé

Polymorphic Typed Defunctionalization

N/A
N/A
Protected

Academic year: 2022

Partager "Polymorphic Typed Defunctionalization"

Copied!
11
0
0

Texte intégral

(1)

Polymorphic Typed Defunctionalization

Fran¸cois Pottier and Nadji Gauthier, INRIA

Outline 1. Closure conversion and defunctionalization 2. Prior art

3. Our approach 4. Closing remarks

(2)

Closure conversion

Closure conversion turns a program that makes use of arbitrary functions into a program where only closed functions (code pointers) are allowed.

λ-abstractions in the source program are encoded as pairs of a code pointer and an environment (closures).

Jλx.eK = (λ({x}, x).JeK,¯ {x})¯ where ¯x is fv(λx.e) Je1 e2K = let(code,env) = Je1Kin code (env,Je2K)

It is known that closure conversion preserves types, provided function types are suitably encoded [Minamide, Morrisett, and Harper, POPL’96]:

1 τ2K = ∃α.((α ×1K 2K) × α)

Closure conversion and defunctionalization 2

(3)

A close cousin: defunctionalization

Defunctionalization [Reynolds, 1972] encodes λ-abstractions as pairs of a tag and an environment, that is, as applications of a data constructor to an environment:

mx.eK = m{x}¯ where ¯x is fv(λx.e)

Function application is encoded as a call to a globally defined function apply...

Je1 e2K = apply Je1K Je2K

... which performs case analysis over m and branches to the appropriate code:

letrecapply = λf .λarg.case f of

| m {x} 7→¯ letx = arg inJeK (* one such clause for every tag m *)

Closure conversion and defunctionalization 3

(4)

Does defunctionalization preserve types?

Imagine the source program contains the functions λsuccx.x + 1 and λnotx.notx, whose types are int int and bool bool. Then, the body of apply contains the following clauses:

| succ 7→ letx = arg inx + 1

| not 7→ letx = arg in notx

In (say) System F, these clauses make incompatible assumptions about arg, and produce results of incompatible types: thus, apply is ill-typed.

Closure conversion and defunctionalization 4

(5)

Prior art: specializing apply

One solution is to split apply into a family of functions, indexed by types:

letrecapplyint→int = λf .λarg.case f of

| succ 7→ let x = arg in x + 1

andapplybool→bool = λf .λarg.casef of

| not 7→ letx = arg in notx

Here, the data constructors succ and not may be declared as follows:

succ : Arrowint→int not : Arrowbool→bool

where Arrowint→int and Arrowbool→bool are distinct algebraic data types.

Prior art 5

(6)

Shortcoming: no polymorphism

In this approach, we have

Je1 e2K = applyτ1→τ2 Je1K Je2K where e1 has type τ1 τ21 τ2K = Arrowτ1→τ2

The trouble is, these definitions only make sense when τ1 τ2 has no free type variables. There is no sensible way of translating

Λα1.Λα2.λf : α1 α2.λx : α1.(f x).

As a result, this approach is applicable in a simply-typed setting only (and, via monomorphization, in the setting of ML).

Prior art 6

(7)

Our approach

In order to translate (f x) where f has type α1 α2, we must have apply : ∀α1α2.Jα1 α2K 1K 2K.

If, furthermore, the type encoding is uniform, then the above implies apply1K Jτ2K : Jτ1 τ2K 1K 2K

for all τ1 and τ2, so this one apply function is in fact suitable for translating arbitrary applications.

Our approach 7

(8)

A uniform type encoding

Let Arrow be a binary algebraic data type constructor, and let

JαK = α

1 τ2K = Arrow1K Jτ2K This yields a uniform type encoding.

Since λsuccx.x + 1 and λnotx.notx have types int int and bool bool, their encodings must have types Arrow int int and Arrow bool bool, respectively. So, we declare:

succ : Arrow int int not : Arrow bool bool

Arrow is a guarded algebraic data type [Xi, Chen, and Chen, POPL’03].

Our approach 8

(9)

Does defunctionalization preserve types? (reconsidered)

The body of apply, enriched with type annotations, is now:

letrecapply : ∀α1α2.Arrow α1 α2 α1 α2 = Λα1.Λα2.λf : Arrow α1 α2.λarg : α1.

casef of

| succ 7→ (* f is succ, so Arrow α1 α2 = Arrow int int holds *) letx = arg inx + 1 : α2

| not 7→ (* f is not, so Arrow α1 α2 = Arrow bool bool holds *) letx = arg in notx : α2

Case analysis over a guarded algebraic data type yields extra type information.

Defunctionalization is now type-preserving.

Our approach 9

(10)

Specialization

One may define versions of apply that are specialized with respect to the types of the parameter and of the result:

applyτ1→τ2 : ∀α.Jτ¯ 1 τ2K 1K 2K where ¯α is ftv(τ1 τ2), or with respect to the number of arguments that are simultaneously available:

applyn : ∀α1 . . . αnαn+1.Jα1 . . . αn αn+1K α1 . . . αn αn+1, or both.

Branches that lead to an inconsistent typing assumption may be pruned—for instance, applyint→int need not check for the tag not. This allows dispatch to be made more efficient based on type information available at the call site.

Closing remarks 10

(11)

Closing remarks

When viewed as a transformation from System F, extended with guarded algebraic data types, into itself, defunctionalization is type-preserving.

Defunctionalization per se is not type-directed, so its correctness may be established using a generic (untyped) simulation argument.

Interesting type-directed optimizations are possible.

This illustrates the usefulness of guarded algebraic data types as a

programming language feature. (Defunctionalization turns Danvy’s [1998]

clever sprintf encoding back to direct style!)

Closing remarks 11

Références

Documents relatifs

Monsieur Jones, le Vice-Président de l’assurance qualité corporative, et un des participants à ces réunions, était d’accord pour désigner le processus d’assurance qualité

Rappelons que l’anneau des entiers de Gauss, Z[i] est euclidien, ce qui im- plique qu’il est factoriel : tout ´el´ement de cet anneau a une d’ecomposi- tion en

[r]

(By convention, these must be ordered in the same way as in the type scheme associated with the data constructor m in the data signature D ′ .) Its value argu- ment is a record

In this talk, I assume that constraints are built on top of equations, so as to remain in the spirit of Hindley and Milner’s type system.. The second motive vanishes; the first

L’ensemble, not´ e B, des bool´ eens a exactement deux ´ el´ ements Vrai, qu’on note V quand on est press´ e.. Faux, qu’on note F quand on est

Il est important de distinguer les variables li´ ees des variables libres mais il n’est jamais important de savoir si deux ´ enonc´ es sont ´ egaux. Ce qui peut ˆ

L’ensemble, not´ e B des bool´ eens a exactement deux ´ el´ ements Vrai, qu’on note V quand on est press´ e.. Faux, qu’on note F quand on est