• No results found

The ( → ) type constructor

5.13 Extensions

5.13.5 The ( → ) type constructor

Haskell allows programmers to use the function arrow, (→), as a type constructor of kindType →Type →Type.72 Here are two examples of how this works:73

-- a class of categories

classCategory (cat::k →k →Type)where id ::∀(a::k).cat a a

(◦) ::∀(a::k) (b::k) (c ::k).cat b c →cat a b →cat a c

-- the instance for (→)

instanceCategory (→)where id x =x

(f ◦ g)x =f (g x)

-- a lightweight reader monad, based on (→)

instanceFunctor ((→)a)where fmap f g x =f (g x)

instanceApplicative ((→)x)where pure x =λ →x

(f <∗>g)x =f x (g x)

instanceMonad ((→)x)where

(f >>=g)x =g (f x)x

Unfortunately, Pico cannot, as written, easily accommodate (→). A non-depen- dent arrow is rightly seen as a degenerate form of Π: the type a →b is the same as

˜

Π_:Rela.b. Without introducing yet a new function type (on top of the six we already

have) and argument syntax, it seems hard to abstract over this degenerate form of Π. Instead, we could add (→) as a new primitive constant with coercions relating it to

˜ Π:

H ::= . . .|(→)

γ ::= . . .|arrowτ1τ2

Σ`tc (→) :∅;a:RelType,b:RelType;Type Tc_Arrow

Σ; Γ`ty τ1 :Type Σ; Γ`ty τ2 :Type

Σ; Γ`coarrowτ1τ2 : (→)τ1τ2 ∼

˜

Πa:Relτ1. τ2 Co_Arrow

The problem we are faced with at this point is consistency. Specifically, we will surely be unable to prove completeness of the rewrite relation (Section 5.10.3) with the

Co_Arrow rule. To repair the damage, we can alter the coercion erasure operation 72The kind of()really is restricted to beTypeTypeType, even though a saturated use

of it can relate unlifted types as well. This oddity is due to be explored, among other dark corners of lifted vs. unlifted types, in a paper I am hoping to write in the next year.

to also rewrite saturated arrow forms to be Π forms, where the following equation is tried before other application forms:

b(→)τ1τ2c =

˜

Πa:Relbτ1c.bτ2c

Now, completeness for Co_Arrow is trivial.

The problem will last surface in the erasure/consistency lemma (Section 5.10.4), which states that whenever bτ1c ∝ bτ2c, we have τ1 ∝τ2. This is now plainly false. We

must assert that arrow forms are consistent with Π-types:

(→)τ1τ2 ∝ ˜ Πa:Relτ1. τ2 C_Arrow1 ˜ Πa:Relτ1. τ2 ∝(→)τ1τ2 C_Arrow2

The definition of ∝ is used in the proof of progress, where now we must consider the possibility of encountering unexpected arrow types. This possibility, though, is dispatched by adding one clause to the canonical forms lemma:

Lemma (Canonical form of arrow types). Σ; Γ6`ty v : (→)τ1τ2

That is, no value has an arrow type, because all λ-forms have Π-types instead. With this in hand, the progress proof should go through unimpeded.

5.14

Conclusion

This chapter is a full consideration of Pico. The detail presented here is intended to be useful to implementors of the language and researchers interested in adapting

Pico to be used as the internal language for a surface language other than Haskell.

I believe Pico is a viable candidate as a general-purpose intermediate language for dependently typed surface languages.

Chapter 6

Type inference and elaboration, or

How to

Bake

a

Pico

Chapter 4 presents the additions to modern Haskell to make it Dependent Haskell, and Chapter 5 presents Pico, the internal language to which we compile Dependent Haskell programs. This chapter formally relates the two languages by defining a type inference/elaboration algorithm,74 Bake, checking Dependent Haskell code and producing a well typed Picoprogram.

At a high level, Bake is unsurprising. It simply combines the ideas of several pieces of prior work [33, 37, 99] and targetsPicoas its intermediate language. Despite its strong basis in prior work, Bakeexhibits a few novelties:

• Perhaps its biggest innovation is how it decides between dependent and non- dependent pattern matching depending on whether the algorithm is in checking or synthesis mode. (See also Section 6.4.)

• It turns out that checking the annotated expression (λ(x:: s)→...) ::∀x →... depends on whether or not the type annotation describes a dependent function. This came as a surprise. See Section 6.6.4.

• The subsumption relation allows an unmatchable function to be subsumed by a matchable one. That is, a function expecting an unmatchable function a→ b

can also accept a matchable one a’→b.

After presenting the elaboration algorithm, I discuss the metatheory in Section 6.8. This section include a soundness result that the Pico program produced by Bake is well typed. It also relates Bake both to OutsideIn and the bidirectional type system (“System SB”) from Eisenberg et al. [33], arguing that Bake is a conservative extension of both.

74I refer to

Bakevariously as an elaboration algorithm, a type inference algorithm, and a type

checking algorithm. This is appropriate, as it is all three. In general, I do not differentiate between these descriptors.

Full statements of all judgments appear in Appendix D, while theorems and definitions, with proofs, appear in Appendix E.