Let Arguments Go First
Ningning Xie

and Bruno C. d. S. Oliveira

The University of Hong Kong
{nnxie,bruno}@cs.hku.hk

Abstract. Bi-directional type checking has proved to be an extremely
useful and versatile tool for type checking and type inference. The conventional presentation of bi-directional type checking consists of twomodes: inference mode and checked mode. In traditional bi-directional
type-checking, type annotations are used to guide (via the checked mode)
the type inference/checking procedure to determine the type of an expression, and type information flows from functions to arguments.
This paper presents a variant of bi-directional type checking where thetype information flows from arguments to functions. This variant retains
the inference mode, but adds a so-called application mode. Such design
can remove annotations that basic bi-directional type checking cannot,
and is useful when type information from arguments is required to typecheck the functions being applied. We present two applications and develop the meta-theory (mostly verified in Coq) of the application mode.

1

Introduction

Bi-directional type checking has been known in the folklore of type systems
for a long time. It was popularized by Pierce and Turner’s work on local type
inference [29]. Local type inference was introduced as an alternative to HindleyMilner (henceforth HM system) type systems [11, 17], which could easily deal
with polymorphic languages with subtyping. Bi-directional type checking is one
component of local type inference that, aided by some type annotations, enables type inference in an expressive language with polymorphism and subtyping. Since Pierce and Turner’s work, various other authors have proved the effectiveness of bi-directional type checking in several other settings, including
many different systems with subtyping [12, 15, 14], systems with dependent
types [37, 10, 2, 21, 3], and various other works [1, 13, 28, 7, 22]. Furthermore,bi-directional type checking has also been combined with HM-style techniques
for providing type inference in the presence of higher-ranked types [27, 14].
The key idea in bi-directional type checking is simple. In its basic form typing
is split into inference and checked modes. The most salient feature of a bidirectional type-checker is when information deduced from inference mode isused to guide checking of an expression in checked mode. One of such interactions
between modes happens in the typing rule for function applications:Γ ` e1 ⇒ A → BΓ ` e2 ⇐ A
APPΓ ` e1 e2 ⇒ B

2In the above rule, which is a standard bi-directional rule for checking applications, the two modes are used. First we synthesize (⇒) the type A → B from e1 ,and then check (⇐) e2 against A, returning B as the type for the application.
This paper presents a variant of bi-directional type checking that employs aso-called application mode. With the application mode the design of the application rule (for a simply typed calculus) is as follows:Γ ` e2 ⇒ A
Γ p Ψ, A ` e1 ⇒ A → B
APPΓ p Ψ ` e1 e2 ⇒ B
In this rule, there are two kinds of judgments. The first judgment is just theusual inference mode, which is used to infer the type of the argument e2 . The
second judgment, the application mode, is similar to the inference mode, but it
has an additional context Ψ . The context Ψ is a stack that tracks the types of
the arguments of outer applications. In the rule for application, the type of theargument e2 is inferred first, and then pushed into Ψ for inferring the type of e1 .
Applications are themselves in the application mode, since they can be in thecontext of an outer application. With the application mode it is possible to infer
the type for expressions such as (λx. x) 1 without additional annotations.
Bi-directional type checking with an application mode may still require type
annotations and it gives different trade-offs with respect to the checked modein terms of type annotations. However the different trade-offs open paths todifferent designs of type checking/inference algorithms. To illustrate the utilityof the application mode, we present two different calculi as applications. The
first calculus is a higher ranked implicit polymorphic type system, which infers
higher-ranked types, generalizes the HM type system, and has polymorphic let
as syntactic sugar. As far as we are aware, no previous work enables an HM-style
let construct to be expressed as syntactic sugar. For this calculus many results
are proved using the Coq proof assistant [9], including type-safety. Moreover asound and complete algorithmic system, inspired by Peyton Jones et al. [27],is also developed. A second calculus with explicit polymorphism illustrates how
the application mode is compatible with type applications, and how it adds
expressiveness by enabling an encoding of type declarations in a System-F-like
calculus. For this calculus, all proofs (including type soundness), are mechanizedin Coq.
We believe that, similarly to standard bi-directional type checking, bi-directional
type checking with an application mode can be applied to a wide range of typesystems. Our work shows two particular and non-trivial applications. Other potential areas of applications are other type systems with subtyping, static overloading, implicit parameters or dependent types.
In summary the contributions of this paper are1 :– A variant of bi-directional type checking where the inference mode is
combined with a new, so-called, application mode. The application modenaturally propagates type information from arguments to the functions.
1

All supplementary materials are available in https://bitbucket.org/ningningxie/
let-arguments-go-first3– A new design for type inference of higher-ranked types which generalizes the HM type system, supports a polymorphic let as syntactic sugar,
and infers higher rank types. We present a syntax-directed specification, anelaboration semantics to System F, some meta-theory in Coq, and an algorithmic type system with completeness and soundness proofs.
– A System-F-like calculus as a theoretical response to the challenge notedby Pierce and Turner [29]. It shows that the application mode is compatible
with type applications, which also enables encoding type declarations. We
present a type system and meta-theory, including proofs of type safety anduniqueness of typing in Coq.

2

Overview

2.1

Background: Bi-Directional Type Checking

Traditional type checking rules can be heavyweight on annotations, in the sensethat lambda-bound variables always need explicit annotations. Bi-directional
type checking [29] provides an alternative, which allows types to propagate downward the syntax tree. For example, in the expression (λf:Int → Int. f) (λy.
y), the type of y is provided by the type annotation on f. This is supported by
the bi-directional typing rule for applications:Γ ` e1 ⇒ A → BΓ ` e2 ⇐ A
APPΓ ` e1 e2 ⇒ B
Specifically, if we know that the type of e1 is a function from A → B, we can checkthat e2 has type A. Notice that here the type information flows from functions
to arguments.
One guideline for designing bi-directional type checking rules [15] is to distinguish introduction rules from elimination rules. Constructs which correspondto introduction forms are checked against a given type, while constructs corresponding to elimination forms infer (or synthesize) their types. For instance,
under this design principle, the introduction rule for pairs is supposed to be inchecked mode, as in the rule Pair-C.
Γ ` e1 ⇐ A
Γ ` e2 ⇐ B
Pair-CΓ ` (e1 , e2 ) ⇐ (A, B)Γ ` e1 ⇒ A
Γ ` e2 ⇒ B
Pair-IΓ ` (e1 , e2 ) ⇒ (A, B)

Unfortunately, this means that the trivial program (1, 2) cannot type-check,
which in this case has to be rewritten to (1, 2) : (Int , Int).
In this particular case, bi-directional type checking goes against its original
intention of removing burden from programmers, since a seemingly unnecessary
annotation is needed. Therefore, in practice, bi-directional type systems do notstrictly follow the guideline, and usually have additional inference rules for the
introduction form of constructs. For pairs, the corresponding rule is Pair-I.
Now we can type check (1, 2), but the price to pay is that two typing rules
for pairs are needed. Worse still, the same criticism applies to other constructs.

4This shows one drawback of bi-directional type checking: often to minimize annotations, many rules are duplicated for having both inference and checked mode,
which scales up with the typing rules in a type system.
2.2

Bi-Directional Type Checking with the Application Mode

We propose a variant of bi-directional type checking with a new application mode.
The application mode preserves the advantage of bi-directional type checking,namely many redundant annotations are removed, while certain programs cantype check with even fewer annotations. Also, with our proposal, the inferencemode is a special case of the application mode, so it does not produce duplicationsof rules in the type system. Additionally, the checked mode can still be easily
combined into the system (see Section 5.1 for details). The essential idea of
the application mode is to enable the type information flow in applications topropagate from arguments to functions (instead of from functions to arguments
as in traditional bi-directional type checking).
To motivate the design of bi-directional type checking with an applicationmode, consider the simple expression(λx. x) 1

This expression cannot type check in traditional bi-directional type checking
because unannotated abstractions only have a checked mode, so annotations are
required. For example, ((λx. x) : Int → Int) 1.
In this example we can observe that if the type of the argument is accounted
for in inferring the type of λx. x, then it is actually possible to deduce that thelambda expression has type Int → Int , from the argument 1.
The Application Mode. If types flow from the arguments to the function, analternative idea is to push the type of the arguments into the typing of thefunction, as the rule that is briefly introduced in Section 1:
Γ ` e2 ⇒ A
Γ p Ψ, A ` e1 ⇒ A → B
APPΓ p Ψ ` e1 e2 ⇒ B
Here the argument e2 synthesizes its type A, which then is pushed into theapplication context Ψ . Lambda expressions can now make use of the application
context, leading to the following rule:Γ, x : A p Ψ ` e ⇒ BLamΓ p Ψ, A ` λx. e ⇒ A → B
The type A that appears last in the application context serves as the type for x,
and type checking continues with a smaller application context and x:A in thetyping context. Therefore, using the rule App and Lam, the expression (λx.
x) 1 can type-check without annotations, since the type Int of the argument 1
is used as the type of the binding x.

5Note that, since the examples so far are based on simple types, obviously
they can be solved by integrating type inference and relying on techniques like
unification or constraint solving. However, here the point is that the application
mode helps to reduce the number of annotations without requiring such sophisticated techniques. Also, the application mode helps with situations where thosetechniques cannot be easily applied, such as type systems with subtyping.
Interpretation of the Application Mode. As we have seen, the guideline for designing bi-directional type checking [15], based on introduction and elimination
rules, is often not enough in practice. This leads to extra introduction rules in
the inference mode. The application mode does not distinguish between introduction rules and elimination rules. Instead, to decide whether a rule should bein inference or application mode, we need to think whether the expression can beapplied or not. Variables, lambda expressions and applications are all examples
of expressions that can be applied, and they should have application mode rules.
However pairs or literals cannot be applied and should have inference rules. For
example, type checking pairs would simply lead to the rule Pair-I. Neverthelesselimination rules of pairs could have non-empty application contexts (see Section 5.2 for details). In the application mode, arguments are always inferred firstin applications and propagated through application contexts. An empty application context means that an expression is not being applied to anything, whichallows us to model the inference mode as a particular case2 .
Partial Type Checking. The inference mode synthesizes the type of an expression,and the checked mode checks an expression against some type. A natural questionis how do these modes compare to application mode. An answer is that, in somesense: the application mode is stronger than inference mode, but weaker thanchecked mode. Specifically, the inference mode means that we know nothing
about the type an expression before hand. The checked mode means that thewhole type of the expression is already known before hand. With the application
mode we know some partial type information about the type of an expression:
we know some of its argument types (since it must be a function type when theapplication context is non-empty), but not the return type.
Instead of nothing or all, this partialness gives us a finer grain notion on
how much we know about the type of an expression. For example, assume
e : A → B → C. In the inference mode, we only have e. In the checked mode, wehave both e and A → B → C. In the application mode, we have e, and maybe
an empty context (which degenerates into inference mode), or an application
context A (we know the type of first argument), or an application context B, A
(we know the types of both arguments).
Trade-offs. Note that the application mode is not conservative over traditional
bidirectional type checking due to the different information flow. However, it
2

Although the application mode generalizes the inference mode, we refer to them as
two different modes. Thus the variant of bi-directional type checking in this paper
is interpreted as a type system with both inference and application modes.

6

provides a new design choice for type inference/checking algorithms, especiallyfor those where the information about arguments is useful. Therefore we next
discuss some benefits of the application mode for two interesting cases where
functions are either variables; or lambda (or type) abstractions.
2.3

Benefits of Information Flowing from Arguments to Functions

Local Constraint Solver for Function Variables. Many type systems, including
type systems with implicit polymorphism and/or static overloading, need information about the types of the arguments when type checking function variables.
For example, in conventional functional languages with implicit polymorphism,
function calls such as (id 3) where id: ∀a. (a → a), are pervasive. In such afunction call the type system must instantiate a to Int. Dealing with such implicit instantiation gets trickier in systems with higher-ranked types. For example,
Peyton Jones et al. [27] require additional syntactic forms and relations, whereas
Dunfield and Krishnaswami [14] add a special purpose application judgment.
With the application mode, all the type information about the arguments being applied is available in application contexts and can be used to solve instantiation constraints. To exploit such information, the type system employs a special
subtyping judgment called application subtyping, with the form Ψ ` A ≤ B. Unlike conventional subtyping, computationally Ψ and A are interpreted as inputs
and B as output. In above example, we have that Int ` ∀a.a → a ≤ B and we
can determine that a = Int and B = Int → Int. In this way, type system is able
to solve the constraints locally according to the application contexts since we nolonger need to propagate the instantiation constraints to the typing process.
Declaration Desugaring for Lambda Abstractions. An interesting consequence of
the usage of an application mode is that it enables the following let sugar:
let x = e1 in e2(λx. e2 ) e1

Such syntactic sugar for let is, of course, standard. However, in the context of
implementations of typed languages it normally requires extra type annotations
or a more sophisticated type-directed translation. Type checking (λx. e2 ) e1
would normally require annotations (for example an annotation for x), or otherwise such annotation should be inferred first. Nevertheless, with the application
mode no extra annotations/inference is required, since from the type of the argument e1 it is possible to deduce the type of x. Generally speaking, with theapplication mode annotations are never needed for applied lambdas. Thus let
can be the usual sugar from the untyped lambda calculus, including HM-style
let expression and even type declarations.
2.4

Application 1: Type Inference of Higher-Ranked Types

As a first illustration of the utility of the application mode, we present a calculus
with implicit predicative higher-ranked polymorphism.

7

Higher-ranked Types. Type systems with higher-ranked types generalize the traditional HM type system, and are useful in practice in languages like Haskell orother ML-like languages. Essentially higher-ranked types enable much of the expressive power of System F, with the advantage of implicit polymorphism. Complete type inference for System F is known to be undecidable [36]. Therefore,
several partial type inference algorithms, exploiting additional type annotations,have been proposed in the past instead [25, 15, 31, 27].
Higher-ranked Types and Bi-directional Type Checking. Bi-directional type checking is also used to help with the inference of higher-ranked types [27, 14]. Consider the following program:(λf. (f 1, f ’c’)) (λx. x)

which is not typeable under those type systems because they fail to infer the typeof f, since it is supposed to be polymorphic. Using bi-directional type checking,we can rewrite this program as((λf. (f 1, f ’c’)) : (∀a. a → a) → (Int, Char)) (λx . x)

Here the type of f can be easily derived from the type signature using checkedmode in bi-directional type checking. However, although some redundant annotations are removed by bi-directional type checking, the burden of inferring
higher-ranked types is still carried by programmers: they are forced to add polymorphic annotations to help with the type derivation of higher-ranked types.
For the above example, the type annotation is still provided by programmers,
even though the necessary type information can be derived intuitively without
any annotations: f is applied to λx. x, which is of type ∀a. a → a.
Generalization. Generalization is famous for its application in let polymorphismin the HM system, where generalization is adopted at let bindings. Let polymorphism is a useful component to introduce top-level quantifiers (rank 1 types)
into a polymorphic type system. The previous example becomes typeable in the
HM system if we rewrite it to: let f = λx. x in (f 1, f ’c’).
Type Inference for Higher-ranked Types with the Application Mode. Using our
bi-directional type system with an application mode, the original expression cantype check without annotations or rewrites: (λf. (f 1, f ’c’)) (λx. x).
This result comes naturally if we allow type information flow from arguments
to functions. For inferring polymorphic types for arguments, we use generalization. In the above example, we first infer the type ∀a. a → a for the argument,
then pass the type to the function. A nice consequence of such an approachis that HM-style polymorphic let expressions are simply regarded as syntactic
sugar to a combination of lambda/application:
let x = e1 in e2(λx. e2 ) e1

With this approach, nested lets can lead to types which are more general than
HM. For example, let s = λx. x in let t = λy. s in e. The type of s is ∀a.
a → a after generalization. Because t returns s as a result, we might expect

8
t: ∀b. b → (∀a. a → a), which is what our system will return. However, HM
will return type t: ∀b. ∀a. b → (a → a), as it can only return rank 1 types,

which is less general than the previous one according to Odersky and Läufer’s
subtyping relation for polymorphic types [24].
Conservativity over the Hindley-Milner Type System. Our type system is a conservative extension over the Hindley-Milner type system, in the sense that every
program that can type-check in HM is accepted in our type system, which is
explained in detail in Section 3.2. This result is not surprising: after desugaring
let into a lambda and an application, programs remain typeable.
Comparing Predicative Higher-ranked Type Inference Systems. We will give afull discussion and comparison of related work in Section 6. Among those works,
we believe the work by Dunfield and Krishnaswami [14], and the work by Peyton Jones et al. [27] are the most closely related work to our system. Both theirsystems and ours are based on a predicative type system: universal quantifiers
can only be instantiated by monotypes. So we would like to emphasize our system’s properties in relation to those works. In particular, here we discuss twointeresting differences, and also briefly (and informally) discuss how the works
compare in terms of expressiveness.
1) Inference of higher-ranked types. In both works, every polymorphic type
inferred by the system must correspond to one annotation provided by the programmer. However, in our system, some higher-ranked types can be inferredfrom the expression itself without any annotation. The motivating expression
above provides an example of this.
2) Where are annotations needed? Since type annotations are useful for inferring higher rank types, a clear answer to the question where annotations areneeded is necessary so that programmers know when they are required to write
annotations. To this question, previous systems give a concrete answer: only on
the binding of polymorphic types. Our answer is slightly different: only on thebindings of polymorphic types in abstractions that are not applied to arguments.
Roughly speaking this means that our system ends up with fewer or smaller
annotations.
3) Expressiveness. Based on these two answers, it may seem that our systemshould accept all expressions that are typeable in their system. However, thisis not true because the application mode is not conservative over traditionalbi-directional type checking. Consider the expression (λf : (∀a. a → a) →(Int, Char). f) (λg. (g 1, g ’a’)), which is typeable in their system. In thiscase, even if g is a polymorphic binding without a type annotation the expression
can still type-check. This is because the original application rule propagates theinformation from the outer binding into the inner expressions. Note that the factthat such expression type-checks does not contradict their guideline of providing
type annotations for every polymorphic binder. Programmers that strictly follow
their guideline can still add a polymorphic type annotation for g. However it doesmean that it is a little harder to understand where annotations for polymorphic

9binders can be omitted in their system. This requires understanding how theapplications in checked mode operate.
In our system the above expression is not typeable, as a consequence of
the information flow in the application mode. However, following our guideline
for annotations leads to a program that can be type-checked with a smaller
annotation: (λf. f) (λg : (∀a. a → a). (g 1, g ’a’)). This means that ourwork is not conservative over their work, which is due to the design choice of theapplication typing rule. Nevertheless, we can always rewrite programs using ourguideline, which often leads to fewer/smaller annotations.
2.5

Application 2: More Expressive Type Applications

The design choice of propagating arguments to functions was subject to consideration in the original work on local type inference [29], but was rejected due topossible non-determinism introduced by explicit type applications:“It is possible, of course, to come up with examples where it would be
beneficial to synthesize the argument types first and then use the resulting information to avoid type annotations in the function part of an
application expression....Unfortunately this refinement does not help infer the type of polymorphic functions. For example, we cannot uniquelydetermine the type of x in the expression (f un[X](x) e) [Int] 3.” [29]Therefore, as a response to this challenge, our second application is a variantof System F. Our development of the calculus shows that the application mode
can actually work well with calculi with explicit type applications. To explain
the new design, consider the expression:(Λa. λx : a. x + 1) Int

which is not typeable in the traditional type system for System F. In System
F the lambda abstractions do not account for the context of possible function
applications. Therefore when type checking the inner body of the lambda abstraction, the expression x + 1 is ill-typed, because all that is known is that x
has the (abstract) type a.
If we are allowed to propagate type information from arguments to functions,then we can verify that a = Int and x + 1 is well-typed. The key insight in the
new type system is to use application contexts to track type equalities inducedby type applications. This enables us to type check expressions such as the bodyof the lambda above (x + 1). Therefore, back to the problematic expression(f un[X](x) e) [Int] 3, the type of x can be inferred as either X or Int since they
are actually equivalent.
Sugar for Type Synonyms. In the same way that we can regard let expressions
as syntactic sugar, in the new type system we further gain built-in type synonyms
for free. A type synonym is a new name for an existing type. Type synonyms
are common in languages such as Haskell. In our calculus a simple form of typesynonyms can be desugared as follows:

10type a = A in e

(Λa. e) A

One practical benefit of such syntactic sugar is that it enables a direct encoding of a System F-like language with declarations (including type-synonyms).
Although declarations are often viewed as a routine extension to a calculus, andare not formally studied, they are highly relevant in practice. Therefore, a more
realistic formalization of a programming language should directly account for
declarations. By providing a way to encode declarations, our new calculus enables a simple way to formalize declarations.
Type Abstraction. The type equalities introduced by type applications may seem
like we are breaking System F type abstraction. However, we argue that typeabstraction is still supported by our System F variant. For example:
let inc = Λa. λx : a. x + 1 in inc Int e(after desugaring) does not type-check, as in a System-F like language. In ourtype system lambda abstractions that are immediatelly applied to an argument,
and unapplied lambda abstractions behave differently. Unapplied lambda abstractions are just like System F abstractions and retain type abstraction. The
example above illustrates this. In contrast the typeable example (Λa. λx : a.
x + 1) Int, which uses a lambda abstraction directly applied to an argument,
can be regarded as the desugared expression for type a = Int in λx : a . x + 1.

3

A Polymorphic Language with Higher-Ranked Types

This section first presents a declarative, syntax-directed type system for a lambda
calculus with implicit higher-ranked polymorphism. The interesting aspects about
the new type system are: 1) the typing rules, which employ a combination ofinference and application modes; 2) the novel subtyping relation under an application context. Later, we prove our type system is type-safe by a type directedtranslation to System F[16, 27] in Section 3.4. Finally an algorithmic type system
is discussed in Section 3.5.
3.1

SyntaxThe syntax of the language is:Expr
e ::= x | n | λx : A. e | λx. e | e1 e2Type
A, B ::= a | A → B | ∀a.A | Int
Monotypeτ ::= a | τ1 → τ2 | Int
Typing Context
Γ ::= ∅ | Γ, x : A
Application Context
Ψ ::= ∅ | Ψ, A

Expressions. Expressions e include variables (x), integers (n), annotated lambda
abstractions (λx : A. e), lambda abstractions (λx. e), and applications (e1 e2 ).
Letters x, y, z are used to denote term variables. Notably, the syntax does notinclude a let expression (let x = e1 in e2 ). Let expressions can be regarded as
the standard syntax sugar (λx. e2 ) e1 , as illustrated in more detail later.

11

Types. Types include type variables (a), functions (A → B), polymorphic types(∀a.A) and integers (Int). We use capital letters (A, B) for types, and small letters(a, b) for type variables. Monotypes are types without universal quantifiers.
Contexts. Typing contexts Γ are standard: they map a term variable x to its
type A. We implicitly assume that all the variables in Γ are distinct. The mainnovelty lies in the application contexts Ψ , which are the main data structureneeded to allow types to flow from arguments to functions. Application contexts
are modeled as a stack. The stack collects the types of arguments in applications.
The context is a stack because if a type is pushed last then it will be popped first.
For example, inferring expression e under application context (a, Int), means e
is now being applied to two arguments e1 , e2 , with e1 : Int, e2 : a, so e should beof type Int → a → A for some A.
3.2

Type System

The top part of Figure 1 gives the typing rules for our language. The judgmentΓ p Ψ ` e ⇒ B is read as: under typing context Γ , and application context Ψ ,e has type B. The standard inference mode Γ ` e ⇒ B can be regarded as a
special case when the application context is empty. Note that the variable names
are assumed to be fresh enough when new variables are added into the typing
context, or when generating new type variables.
Rule T-Var says that if x : A is in the typing context, and A is a subtype of
B under application context Ψ , then x has type B. It depends on the subtyping
rules that are explained in Section 3.3. Rule T-Int shows that integer literals
are only inferred to have type Int under an empty application context. This is
obvious since an integer cannot accept any arguments.
T-Lam shows the strength of application contexts. It states that, without
annotations, if the application context is non-empty, a type can be popped fromthe application context to serve as the type for x. Inference of the body thencontinues with the rest of the application context. This is possible, because theexpression λx. e is being applied to an argument of type A, which is the type atthe top of the application context stack. Rule T-Lam2 deals with the case when
the application context is empty. In this situation, a monotype τ is guessed forthe argument, just like the Hindley-Milner system.
Rule T-LamAnn1 works as expected with an empty application context: a
new variable x is put with its type A into the typing context, and inference
continues on the abstraction body. If the application context is non-empty, thenthe rule T-LamAnn2 applies. It checks that C is a subtype of A before puttingx : A in the typing context. However, note that it is always possible to remove
annotations in an abstraction if it has been applied to some arguments.
Rule T-App pushes types into the application context. The application rule
first infers the type of the argument e2 with type A. Then the type A is generalized in the same way that types in let expressions are generalized in the HM
type system. The resulting generalized type is B. The generalization is shownin rule T-Gen, where all free type variables are extracted to quantifiers. Thus12Γ pΨ `e ⇒ B
x:A∈ΓΨ ` A <: B
T-VarΓp Ψ ` x ⇒ BΓ ` n ⇒ Int

T-Int

Γ, x : τ ` e ⇒ B

Γ, x : A p Ψ ` e ⇒ BΓ p Ψ, A ` λx. e ⇒ A → B

T-Lam

Γ ` λx. e ⇒ τ → B

T-Lam2Γ, x : A ` e ⇒ BΓ ` λx : A. e ⇒ A → B
C <: A

T-LamAnn1

Γ, x : A p Ψ ` e ⇒ BΓ ` e2 ⇒ A

a = f tv(A) − f tv(Γ )T-LamAnn2

Γ p Ψ, C ` λx : A. e ⇒ C → B

T-Gen

Γgen (A) = ∀a.A

Γ p Ψ, B ` e1 ⇒ B → CΓgen (A) = B

T-AppΓ p Ψ ` e1 e2 ⇒ C
A <: BA <: BInt <: Int

S-Int

a <: aAJa 7→ τ K <: B
∀a.A <: B

S-Var

A <: ∀a.B
C <: AS-ForallL

S-ForallR

B <: DA → B <: C → D

S-FunΨ ` A <: B

∅ ` A <: A

S-Empty

C <: AΨ, C ` AJa 7→ τ K <: B
Ψ, C ` ∀a.A <: B

S-ForallL2

Ψ ` B <: D

Ψ, C ` A → B <: C → D

S-Fun2

Fig. 1. Syntax-directed typing and subtyping.

the type of e1 is now inferred under an application context extended with type
B. The generalization step is important to infer higher ranked types: since B
is a possibly polymorphic type, which is the argument type of e1 , then e1 is ofpossibly a higher rank type.
Let Expressions. The language does not have built-in let expressions, but instead supports let as syntactic sugar. The typing rule for let expressions in the
HM system is (without the gray-shaded part):Γ ` e 1 ⇒ A1

Γgen (A1 ) = A2

Γ, x : A2 p Ψ ` e2 ⇒ B

Γ p Ψ ` let x = e1 in e2 ⇒ B

T-Let

13

where we do generalization on the type of e1 , which is then assigned as thetype of x while inferring e2 . Adapting this rule to our system with application
contexts would result in the gray-shaded part, where the application context isonly used for e2 , because e2 is the expression being applied. If we desugar the let
expression (let x = e1 in e2 ) to ((λx. e2 ) e1 ), we have the following derivation:Γ ` e 1 ⇒ A1

Γ, x : A2 p Ψ ` e2 ⇒ B
T-Lam
Γ p Ψ, A2 ` λx. e2 ⇒ A2 → BΓgen (A1 ) = A2
T-App
Γ p Ψ ` (λx. e2 ) e1 ⇒ B

The type A2 is now pushed into application context in rule T-App, and thenassigned to x in T-Lam. Comparing this with the typing derivations with rule
T-Let, we now have same preconditions. Thus we can see that the rules in
Figure 1 are sufficient to express an HM-style polymorphic let construct.
Meta-theory. The type system enjoys several interesting properties, especiallylemmas about application contexts. Before we present those lemmas, we need ahelper definition of what it means to use arrows on application contexts.
Definition 1 (Ψ → B). If Ψ = A1 , A2 , ..., An , then Ψ → B means the function
type An → ... → A2 → A1 → B.
Such definition is useful to reason about the typing result with application
contexts. One specific property is that the application context determines theform of the typing result.
Lemma 1 (Ψ Coincides with Typing Results). If Γ p Ψ ` e ⇒ A, thenfor some A0 , we have A = Ψ → A0 .
Having this lemma, we can always use the judgment Γ p Ψ ` e ⇒ Ψ → A0
instead of Γ p Ψ ` e ⇒ A.
In traditional bi-directional type checking, we often have one subsumption
rule that transfers between inference and checked mode, which states that if an
expression can be inferred to some type, then it can be checked with this type. In
our system, we regard the normal inference mode Γ ` e ⇒ A as a special case,when the application context is empty. We can also turn from normal inferencemode into application mode with an application context.
Lemma 2 (Subsumption). If Γ ` e ⇒ Ψ → A, then Γ p Ψ ` e ⇒ Ψ → A.
The relationship between our system and standard Hindley Milner type system can be established through the desugaring of let expressions. Namely, if e is
typeable in Hindley Milner system, then the desugared expression |e| is typeablein our system, with a more general typing result.
Lemma 3 (Conservative over HM). If Γ `HM e ⇒ A, then for some B,
we have Γ ` |e| ⇒ B, and B <: A.

14

3.3

Subtyping

We present our subtyping rules at the bottom of Figure 1. Interestingly, oursubtyping has two different forms.
Subtyping. The first judgment follows Odersky and Läufer [24]. A <: B meansthat A is more polymorphic than B and, equivalently, A is a subtype of B. Rules
S-Int and S-Var are trivial. Rule S-ForallR states A is subtype of ∀a.B only
if A is a subtype of B, with the assumption a is a fresh variable. Rule S-ForallLsays ∀a.A is a subtype of B if we can instantiate it with some τ and show theresult is a subtype of B. In rule S-Fun, we see that subtyping is contra-variant
on the argument type, and covariant on the return type.
Application Subtyping. The typing rule T-Var uses the second subtyping judgment Ψ ` A <: B. To motivate this new kind of judgment, consider the expression id 1 for example, whose derivation is stuck at T-Var (here we assumeid : ∀a.a → a ∈ Γ ):

Γ ` 1 ⇒ Int

id : ∀a.a → a ∈ ΓΓ p Int ` id ⇒Γgen (Int) = IntΓ ` id 1 ⇒

???
T-Var
T-App

Here we know that id : ∀a.a → a and also, from the application context, that
id is applied to an argument of type Int. Thus we need a mechanism for solving
the instantiation a = Int and return a supertype Int → Int as the type of id. Thisis precisely what the application subtyping achieves: resolve instantiation constraints according to the application context. Notice that unlike existing works[27, 14], application subtyping provides a way to solve instantiation more locally,
since it does not mutually depend on typing.
Back to the rules in Figure 1, one way to understand the judgment Ψ `A <: B from a computational point-of-view is that the type B is a computed
output, rather than an input. In other words B is determined from Ψ and A. Thisis unlike the judgment A <: B, where both A and B would be computationallyinterpreted as inputs. Therefore it is not possible to view A <: B as a special
case of Ψ ` A <: B where Ψ is empty.
There are three rules dealing with application contexts. Rule S-Empty isfor case when the application context is empty. Because it is empty, we have noconstraints on the type, so we return it back unchanged. Note that this is where
HM systems (also Peyton Jones et al. [27]) would normally use a rule Inst to
remove top-level quantifiers:∀a.A <: AJa 7→ τ K

Inst

Our system does not need Inst, because in applications, type information flows
from arguments to the function, instead of function to arguments. In the latter
case, Inst is needed because a function type is wanted instead of a polymorphic
type. In our approach, instantiation of type variables is avoided unless necessary.

15

The two remaining rules apply when the application context is non-empty,
for polymorphic and function types respectively. Note that we only need todeal with these two cases because Int or type variables a cannot have a nonempty application context. In rule S-Forall2, we instantiate the polymorphic
type with some τ , and continue. This instantiation is forced by the application
context. In rule S-Fun2, one function of type A → B is now being applied to anargument of type C. So we check C <: A. Then we continue with B and therest application context, and return C → D as the result type of the function.
Meta-theory. Application subtyping is novel in our system, and it enjoys someinteresting properties. For example, similarly to typing, the application context
decides the form of the supertype.
Lemma 4 (Ψ Coincides with Subtyping Results). If Ψ ` A <: B, thenfor some B 0 , B = Ψ → B 0 .
Therefore we can always use the judgment Ψ ` A <: Ψ → B 0 , instead of Ψ `A <: B. Application subtyping is also reflexive and transitive. Interestingly,
in those lemmas, if we remove all applications contexts, they are exactly thereflexivity and transitivity of traditional subtyping.
Lemma 5 (Reflexivity). Ψ ` Ψ → A <: Ψ → A.
Lemma 6 (Transitivity). If Ψ1 ` A <: Ψ1 → B, and Ψ2 ` B <: Ψ2 → C,then Ψ2 , Ψ1 ` A <: Ψ1 → Ψ2 → C.
Finally, we can convert between subtyping and application subtyping. We
can remove the application context and still get a subtyping relation:
Lemma 7 (Ψ ` <: to <:). If Ψ ` A <: B, then A <: B.
Transferring from subtyping to application subtyping will result in a moregeneral type.
Lemma 8 (<: to Ψ ` <:). If A <: Ψ → B1 , then for some B2 , we haveΨ ` A <: Ψ → B2 , and B2 <: B1 .
This lemma may not seem intuitive at first glance. Consider a concrete example Int → ∀a.a <: Int → Int, and Int ` Int → ∀a.a <: Int → ∀a.a. The
former one, holds because we have ∀a.a <: Int in the return type. But in thelatter one, after Int is consumed from application context, we eventually reach
S-Empty, which always returns the original type back.
3.4

Translation to System F, Coherence and Type-Safety

We translate the source language into a variant of System F that is also used in
Peyton Jones et al. [27]. The translation is shown to be coherent and type safe.
Due to space limitations, we only summarize the key aspects of the translation.
Full details can be found in the supplementary materials of the paper.
The syntax of our target language is as follows:16

Expressions s, f ::= x | n | λx : A. s | Λa.s | s1 s2 | s1 A
In the translation, we use f to refer to the coercion function produced by
the subtyping translation, and s to refer to the translated term in System F. We
write Γ `F s : A to mean the term s has type A in System F.
The type-directed translation follows the rules in Figure 1, with a translation
output in the forms of judgments. We summarize all judgments as:
Judgment
A <: BfΨ ` A <: Bf
Γ pΨ `e ⇒ A
sTranslation Output
coercion function fcoercion function ftarget expression s

Soundness∅ `F f : A → B∅ `F f : A → B
Γ `F s : A

For example, A <: B
f means that if A <: B holds in the source language, we can translate it into a System F term f , which is a coercion function
and has type A → B. We prove that our system is type safe by proving that thetranslation produces well-typed terms.
Lemma 9 (Typing Soundness). If Γ p Ψ ` e ⇒ As, then Γ `F s : A.

However, there could be multiple targets corresponding to one expression due
to the multiple choices for τ . To prove that the translation is coherent, we provethat all the translations for one expression have the same operational semantics.
We write |e| for the expressions after type erasure since types are useless after
type checking. Because multiple targets could have different number of coercion
functions, we use η-id equality [5] instead of syntactic equality, where two expressions are regarded as equivalent if they can turn into the same expression
through η-reduction or removal of redundant identity functions. We then provethat our translation actually generates a unique target:
Lemma 10 (Coherence). If Γ1 p Ψ1 ` e ⇒ Athen |s1 | ηid |s2 |.
3.5

s1 , and Γ2 p Ψ2 ` e ⇒ B

Algorithmic System

Even though our specification is syntax-directed, it does not directly lead to analgorithm, because there are still many guesses in the system, such as in ruleT-Lam2. This subsection presents a brief introduction of the algorithm, which
essentially follows the approach by Peyton Jones et al. [27]. Full details can befound in the supplementary materials.
Instead of guessing, the algorithm creates meta type variables αb, βb which are
waiting to be solved. The judgment for the algorithmic type system is (S0 , N0 ) pΓ p Ψ ` e ⇒ A ,→ (S1 , N1 ). Here we use N as name supply, from which we
can always extract new names. We use S as a notation for the substitution thatmaps meta type variables to their solutions. For example, rule T-Lam2 becomes(S0 , N0 ) p Γ, x : βb ` e ⇒ A ,→ (S1 , N1 )AT-Lam1b p Γ ` λx. e ⇒ βb → A ,→ (S1 , N1 )(S0 , N0 β)

s2 ,

17

Comparing it to rule T-Lam2, τ is replaced by a new meta type variable βbb But despite of the name supply and substitution, thefrom name supply N0 β.
rule retains the structure of T-Lam2.
Having the name supply and substitutions, the algorithmic system is a direct
extension of the specification in Figure 1, with a process to do unifications thatsolve meta type variables. Such unification process is quite standard and similar
to the one used in the Hindley-Milner system. We proved our algorithm is soundand complete with respect to the specification.
Theorem 1 (Soundness). If ([], N0 ) p Γ ` e ⇒ A ,→ (S1 , N1 ), then for any
substitution V with dom(V ) = fmv (S1 Γ, S1 A), we have V S1 Γ ` e ⇒ V S1 A.
Theorem 2 (Completeness). If Γ ` e ⇒ A, then for a fresh N0 , we have([], N0 ) p Γ ` e ⇒ B ,→ (S1 , N1 ), and for some S2 , we have Γ (S2 S1 B) <: Γ (A).

4More Expressive Type Applications

This section presents a System-F-like calculus, which shows that the application
mode not only does work well for calculi with explicit type applications, but it
also adds interesting expressive power, while at the same time retaining uniqueness of types for explicitly polymorphic functions. One additional novelty in thissection is to present another possible variant of typing and subtyping rules forthe application mode, by exploiting the lemmas presented in Sections 3.2 and 3.3.
4.1

Syntax

We focus on a new variant of the standard System F. The syntax is as follows:Expre ::= x | n | λx : A. e | λx. e | e1 e2 | Λa.e | e [A]Type
A ::= a | Int | A → B | ∀a.A
Typing Context
Γ ::= ∅ | Γ, x : A | Γ, a | Γ, a = A
Application Context Ψ ::= ∅ | Ψ, A | Ψ, [A]The syntax is mostly standard. Expressions include variables x, integers n,annotated abstractions λx : A. s, unannotated abstractions λx. e, applications
e1 e2 , type abstractions Λa.s, and type applications e1 [A]. Types includes typevariable a, integers Int, function types A → B, and polymorphic types ∀a.A.
The main novelties are in the typing and application contexts. Typing contexts contain the usual term variable typing x : A, type variables a, and typeequations a = A, which track equalities and are not available in System F. Application contexts use A for the argument type for term-level applications, anduse [A] for the type argument itself for type applications.
Applying Contexts. The typing contexts contain type equations, which can beused as substitutions. For example, a = Int, x : Int, b = Bool can be applied toa → b to get the function type Int → Bool . We write hΓ iA for Γ applied as a
substitution to type A. The formal definition is given in Figure 2.

18h∅iA = AhΓ, aiA = hΓ iA

hΓ, x : BiA = hΓ iA
hΓ, a = BiA = hΓ i(AJa 7→ BK)

Fig. 2. Apply contexts as substitutions on types.

a∈Γ
Γ `a

Γ `A
WF-TVarΓ ` Int

WF-IntΓ `BΓ `A→BΓ, a ` A
WF-ArrowΓ ` ∀a.A

WF-AllFig. 3. Well-formedness.

Well-formedness. The type well-formedness under typing contexts is given in
Figure 3, which is quite straightforward. Notice that there is no rule corresponding to type variables in type equations. For example, a is not a well-formed typeunder typing context a = Int, instead, ha = Intia is. In other words, we keep theinvariant: types are always fully substituted under the typing context.
The well-formedness of typing contexts Γ ctx , and the well-formedness ofapplication contexts Γ ` Ψ can be defined naturally based on the well-formednessof types. The specific definitions can be found in the supplementary materials.
4.2

Type System

Typing Judgments. From Lemma 1 and Lemma 4, we know that the application
context always coincides with typing/subtyping results. This means that thetypes of the arguments can be recovered from the application context. So insteadof the whole type, we can use only the return type as the output type. For
example, we review the rule T-Lam in Figure 1:
Γ, x : A p Ψ ` e ⇒ B
T-LamΓ p Ψ, A ` λx. e ⇒ A → B

Γ, x : A p Ψ ` e ⇒ C
T-Lam-AltΓ p Ψ, A ` λx. e ⇒ C

We have B = Ψ → C for some C by Lemma 1. Instead of B, we can directlyreturn C as the output type, since we can derive from the application context
that e is of type Ψ → C, and λx. e is of type (Ψ, A) → C. Thus we obtain the
T-Lam-Alt rule.
Note that the choice of the style of the rules is only a matter of taste in thelanguage in Section 3. However, it turns out to be very useful for our variant of
System F, since it helps avoiding introducing types like ∀a = Int.a. Therefore,
we adopt the new form of judgment. Now the judgment Γ p Ψ ` e ⇒ A isinterpreted as: under the typing context Γ , and the application context Ψ , thereturn type of e applied to the arguments whose types are in Ψ is A.
Typing Rules. Using the new interpretation of the typing judgment, we give thetyping rules in the top of Figure 4. SF-Var depends on the subtyping rules.

19Γ pΨ `e ⇒ B
Γ ctx

Γ `Ψ

x:A∈Γ

Ψ ` A <: B

Γ ctx
SF-VarΓ pΨ `x ⇒ BΓ ` n ⇒ Int

SF-IntΓ, x : hΓ iA ` e ⇒ BΓ ` λx : A. e ⇒ hΓ iA → B
Γ, x : hΓ iA p Ψ ` e ⇒ B

Γ, x : A p Ψ ` e ⇒ BΓ p Ψ, hΓ iA ` λx : A. e ⇒ BΓ ` e2 ⇒ A

SF-LamAnn1

SF-LamAnn2

Γ p Ψ, A ` λx. e ⇒ B
Γ, a ` e ⇒ BΓ p Ψ, A ` e1 ⇒ B
SF-AppΓ p Ψ ` e1 e2 ⇒ B
Γ, a = A p Ψ ` e ⇒ BΓ p Ψ, [A] ` Λa.e ⇒ B

SF-LamΓ ` Λa.e ⇒ ∀a.B

SF-TLam1

Γ p Ψ, [hΓ iA] ` e ⇒ B
SF-TLam2

Γ p Ψ ` e [A] ⇒ B

SF-TAppΨ ` A <: B
∅ ` A <: AΨ ` BJa 7→ AK <: CΨ, [A] ` ∀a.B <: C

SF-SEmptyΨ ` B <: C
SF-STAppΨ, A ` A → B <: C

SF-SApp

Fig. 4. Type system for the new System F variant.

Rule SF-Int always infers integer types. Rule SF-LamAnn1 first applies current context on A, then puts x : hΓ iA into the typing context to infer e. The
return type is a function type because the application context is empty. Rule
SF-LamAnn2 has a non-empty application context, so it requests that the typeat the top of the application context is equivalent to hΓ iA. The output typeis B instead of a function type. Notice how the invariant that types are fully
substituted under the typing context is preserved in these two rules.
Rule SF-Lam pops the type A from the application context, puts x : A into
the typing context, and returns only the return type B. In rule SF-App, theargument type A is pushed into the application context for inferring e1 , so the
output type B is the type of e1 under application context (Ψ, A), which is exactly
the return type of e1 e2 under Ψ .
Rule SF-TLam1 is for type abstractions. The type variable a is pushed
into the typing context, and the return type is a polymorphic type. In rule SFTLam2, the application context has the type argument A at its top, which means
the type abstraction is applied to A. We then put the type equation a = A into
the typing context to infer e. Like term-level applications, here we only returnthe type B instead of a polymorphic type. In rule SF-TApp, we first apply the

20

typing context on the type argument A, then we put the applied type argumenthΓ iA into the application context to infer e, and return B as the output type.
Subtyping. The definition of subtyping is given at the bottom of Figure 4. As withthe typing rules, the part of argument types corresponding to the application
context is omitted in the output. We interpret the rule form Ψ ` A <: B as,
under the application context Ψ , A is a subtype of the type whose type argumentsare Ψ and the return type is B.
Rule SF-SEmpty returns the input type under the empty application context. Rule SF-STApp instantiates a with the type argument A, and returns C.
Note how application subtyping can be extended naturally to deal with typeapplications. Rule SF-SApp requests that the argument type is the same as the
top type in the application context, and returns C.
4.3

Meta Theory

Applying the idea of the application mode to System F results in a well-behaved
type system. For example, subtyping transitivity becomes more concise:
Lemma 11 (Subtyping Transitivity). If Ψ1 ` A <: B, and Ψ2 ` B <: C,then Ψ2 , Ψ1 ` A <: C.
Also, we still have the interesting subsumption lemma that transfers from theinference mode to the application mode:
Lemma 12 (Subsumption). If Γ ` e ⇒ A, and Γ ` Ψ , and Ψ ` A <: B,
then Γ p Ψ ` e ⇒ B.
Furthermore, we prove the type safety by proving the progress lemma andthe preservation lemma. The detailed definitions of operational semantics andvalues can be found in the supplementary materials.
Lemma 13 (Progress). If ∅ ` e ⇒ T , then either e is a value, or there exists
e0 , such that e −→ e0 .
Lemma 14 (Preservation). If Γ p Ψ ` e ⇒ A, and e −→ e0 , then Γ p Ψ `e0 ⇒ A.
Moreover, introducing type equality preserves unique types:
Lemma 15 (Uniqueness of typing). If Γ p Ψ ` e ⇒ A, and Γ p Ψ `e ⇒ B, then A = B.

5

Discussion

This section discusses possible design choices regarding bi-directional type checking with the application mode, and talks about possible future work.

21

5.1

Combining Application and Checked Modes

Although the application mode provides us with alternative design choices ina bi-directional type system, a checked mode can still be easily added. One
motivation for the checked mode would be annotated expressions e : A, where
the type of expressions is known and is therefore used to check expressions.
Consider adding e : A for introducing the third checked mode for the language in Section 3. Notice that, since the checked mode is stronger than application mode, when entering checked mode the application context is no longer
useful. Instead we use application subtyping to satisfy the application context
requirements. A possible typing rule for annotation expressions is:Ψ ` A <: BΓ `e ⇐ A
T-Ann
Γ p Ψ ` (e : A) ⇒ B
Here, e is checked using its annotation A, and then we instantiate A to B using
subtyping with application context Ψ .
Now we can have a rule set of the checked mode for all expressions. For
example, one useful rule for abstractions in checked mode could be Abs-Chk,
where the parameter type A serves as the type of x, and typing checks the
body with B. Also, combined with the information flow, the checked rule forapplication checks the function with the full type.
Γ,x : A ` e ⇐ B
Abs-ChkΓ ` λx. e ⇐ A → BΓ ` e2 ⇒ AΓ ` e1 ⇐ A → B
App-ChkΓ ` e1 e2 ⇐ B

Note that adding expression annotations might bring convenience for programmers, since annotations can be more freely placed in a program. For example, (λf. f 1) : (Int → Int) → Int becomes valid. However this does not add
expressive power, since programs that are typeable under expression annotations,would remain typeable after moving the annotations to bindings. For example
the previous program is equivalent to (λf : (Int → Int). f 1).
This discussion is a sketch. We have not defined the corresponding declarative
system nor algorithm. However we believe that the addition of a checked mode
will not bring surprises to the meta-theory.
5.2

Additional Constructs

In this section, we show that the application mode is compatible with otherconstructs, by discussing how to add support for pairs in the language given
in Section 3. A similar methodology would apply to other constructs like sum
types, data types, if-then-else expressions and so on.
The introduction rule for pairs must be in the inference mode with an empty
application context. Also, the subtyping rule for pairs is as expected.
Γ ` e1 ⇒ A
Γ ` e2 ⇒ B
T-PairΓ ` (e1 , e2 ) ⇒ (A, B)

A1 <: B1A2 <: B2
S-Pair(A1 , A2 ) <: (B1 , B2 )

22

The application mode can apply to the elimination constructs of pairs. If one
component of the pair is a function, for example, (fst (λx. x, 3) 4), then it ispossible to have a judgment with a non-empty application context. Therefore,
we can use the application subtyping to account for the application contexts:
Γ ` e ⇒ (A, B)Ψ ` A <: C
T-Fst1
Γ p Ψ ` fst e ⇒ CΓ ` e ⇒ (A, B)Ψ ` B <: C
T-Snd1
Γ p Ψ ` snd e ⇒ C

However, in polymorphic type systems, we need to take the subsumption rule
into consideration. For example, in the expression (λx : (∀a.(a, b)). fst x), fst
is applied to a polymorphic type. Interestingly, instead of a non-deterministic
subsumption rule, having polymorphic types actually leads to a simpler solution.
According to the philosophy of the application mode, the types of the arguments
always flow into the functions. Therefore, instead of regarding (fst e) as an
expression form, where e is itself an argument, we could regard fst as a function
on its own, whose type is (∀ab.(a, b) → a). Then as in the variable case, we use
the subtyping rule to deal with application contexts. Thus the typing rules for
fst and snd can be modeled as:Ψ ` (∀ab.(a, b) → a) <: A
T-Fst2
Γ p Ψ ` fst ⇒ AΨ ` (∀ab.(a, b) → b) <: A
T-Snd2
Γ p Ψ ` snd ⇒ A

Note that another way to model those two rules would be to simply have an
initial typing environment Γinitial ≡ fst : (∀ab.(a, b) → a), snd : (∀ab.(a, b) → b).
In this case the elimination of pairs be dealt directly by the rule for variables.
An extended version of the calculus presented in Section 3, which includes
the rules for pairs (T-Pair, S-Pair, T-Fst2 and T-Snd2), has been formallystudied. All the theorems presented in Section 3 hold with the extension of pairs.
5.3

Dependent Type Systems

One remark about the application mode is that the same idea is possibly applicable to systems with advanced features, where type inference is sophisticatedor even undecidable. One promising application is, for instance, dependent typesystems [37, 10, 2, 21, 3]. Type systems with dependent types usually unify thesyntax for terms and types, with a single lambda abstraction generalizing bothtype and lambda abstractions. Unfortunately, this means that the let desugar
is not valid in those systems. As a concrete example, consider desugaring theexpression let a = Int in λx : a. x + 1 into (λa. λx : a. x + 1) Int, which is illtyped because the type of x in the abstraction body is a and not Int.
Because let cannot be encoded, declarations cannot be encoded either. Modeling declarations in dependently typed languages is a subtle matter, and normally requires some additional complexity [34].
We believe that the same technique presented in Section 4 can be adapted
into a dependently typed language to enable a let encoding. In a dependent typesystem with unified syntax for terms and types, we can combine the two forms

23in the typing context (x : A and a = A) into a unified form x = e : A. Then
we can combine two application rules SF-App and SF-TApp into De-App, andalso two abstraction rules SF-Lam and SF-TLam1 into De-Lam.
Γ ` e2 ⇒ A
Γ p Ψ, e2 : A ` e1 ⇒ B
De-AppΓ p Ψ ` e1 e2 ⇒ B

Γ, x = e1 : A p Ψ ` e ⇒ B
De-LamΓ p Ψ, e1 : A ` λx. e ⇒ B

With such rules it would be possible to handle declarations easily in dependent type systems. Note this is still a rough idea and we have not fully worked
out the typing rules for this type system yet.

6

Related Work

6.1

Bi-Directional Type Checking

Bi-directional type checking was popularized by the work of Pierce and Turner[29]. It has since been applied to many type systems with advanced features. The
alternative application mode introduced by us enables a variant of bi-directional
type checking. There are many other efforts to refine bi-directional type checking.
Colored local type inference [25] refines local type inference for explicit polymorphism by propagating partial type information. Their work is built on distinguishing inherited types (known from the context) and synthesized types (inferred from terms). A similar distinction is achieved in our algorithm by manipulating type variables [14]. Also, their information flow is from functions toarguments, which is fundamentally different from the application mode.
The system of tridirectional type checking [15] is based on bi-directional typechecking and has a rich set of property types including intersections, unions andquantified dependent types, but without parametric polymorphism. Tridirectional type checking has a new direction for supporting type checking unions
and existential quantification. Their third mode is basically unrelated to ourapplication mode, which propagates information from outer applications.
Greedy bi-directional polymorphism [13] adopts a greedy idea from Cardelli[4] on bi-directional type checking with higher ranked types, where the typevariables in instantiations are determined by the first constraint. In this way,
they support some uses of impredicative polymorphism. However, the greedinessalso makes many obvious programs rejected.
6.2Type Inference for Higher-Ranked Types

As a reference, Figure 5 [20, 14] gives a high-level comparison between related
works and our system.
Predicative Systems. Peyton Jones et al. [27] developed an approach for type inference for higher rank types using traditional bi-directional type checking basedon Odersky and Läufer [24]. However in their system, in order to do instantiation on higher rank types, they are forced to have an additional type category (ρ24System
MLF
HML
FPH

Types
Impred Let
flexible and rigid yes
yes
flexible F-types yes
yesboxy F-types
yes
yes

Peyton Jones F-types
et al. (2007)
Dunfield et al. F-types(2013)
this paper
F-typesnoyes

Annotations
on polymorphically used parameterson polymorphic parameterson polymorphic parameters and somelet bindings with higher-ranked types
on polymorphic parametersnono

on polymorphic parametersnosugar on polymorphic parameters that are
not applied

Fig. 5. Comparison of higher-ranked type inference systems.

types) as a special kind of higher rank type without top-level quantifiers. This
complicates their system since they need to have additional rule sets for suchtypes. They also combine a variant of the containment relation from Mitchell[23] for deep skolemisation in subsumption rules, which we believe is compatible
with our subtyping definition.
Dunfield and Krishnaswami [14] build a simple and concise algorithm forhigher ranked polymorphism based on traditional bidirectional type checking.
They deal with the same language of Peyton Jones et al. [27], except they do
not have let expressions nor generalization (though it is discussed in design
variations). They have a special application judgment which delays instantiation
until the expression is applied to some argument. As with application mode, thisavoids the additional category of types. Unlike their work, our work supports
generalization and HM-style let expressions. Moreover the use of an application
mode in our work introduces several differences as to when and where annotations are needed (see Section 2.4 for related discussion).
Impredicative Systems. MLF [18, 32, 19] generalizes ML with first-class polymorphism. MLF introduces a new type of bounded quantification (either rigid
or flexible) for polymorphic types so that instantiation of polymorphic bindings
is delayed until a principal type is found. The HML system [20] is proposed asa simplification and restriction of MLF . HML only uses flexible types, whichsimplifies the type inference algorithm, but retains many interesting properties
and features.
The FPH system [35] introduces boxy monotypes into System F types. One
critique of boxy type inference is that the impredicativity is deeply hidden in thealgorithmic type inference rules, which makes it hard to understand the interaction between its predicative constraints and impredicative instantiations [31].
6.3

Tracking Type Equalities

Tracking type equalities is useful in various situations. Here we discuss specifically two related cases where tracking equalities plays an important role.

25Type Equalities in Type Checking. Tracking type equalities is one essential
part for type checking algorithms involving Generalized Algebraic Data Types(GADTs) [6, 26, 33]. For example, Peyton Jones et al. [26] propose a type inference algorithm based on unification for GADTs, where type equalities only apply
to user-specified types. However, reasoning about type equalities in GADTs isessentially different from the approach in Section 4: type equalities are introduced by pattern matches in GADTs, while they are introduced through typeapplications in our system. Also, type equalities in GADTs are local, in thesense different branches in pattern matches have different type equalities for thesame type variable. In our system, a type equality is introduced globally andis never changed. However, we believe that they can be made compatible bydistinguishing different kinds of equalities.
Equalities in Declarations. In systems supporting dependent types, type equalities can be introduced by declarations. In the variant of pure type systems proposed by Severi and Poll [34], expressions x = a : A in b generate an equality
x = a : A in the typing context, which can be fetched later through δ-reduction.
However, δ-reduction rules require careful design, and the conversion rule of δreduction makes the type system non-deterministic. One potential usage of theapplication mode is to help reduce the complexity for introducing declarationsin those type systems, as briefly discussed in Section 5.3.

7

Conclusion

We proposed a variant of bi-directional type checking with a new applicationmode, where type information flows from arguments to functions in applications.
The application mode is essentially a generalization of the inference mode, cantherefore work naturally with inference mode, and avoid the rule duplicationthat is often needed in traditional bi-directional type checking. The application
mode can also be combined with the checked mode, but this often does not
add expressiveness. Compared to traditional bi-directional type checking, theapplication mode opens a new path to the design of type inference/checking.
We have adopted the application mode in two type systems. Those two systems enjoy many interesting properties and features. However as bi-directional
type checking can be applied to many type systems, we believe application mode
is applicable to various type systems. One obvious potential future work is toinvestigate more systems where the application mode brings benefits. This includes systems with subtyping, intersection types [30, 8], static overloading, or
dependent types.

Acknowledgements
We thank the anonymous reviewers for their helpful comments. This work has
been sponsored by the Hong Kong Research Grant Council projects number
17210617 and 17258816.

Bibliography[1] Andreas Abel. Termination checking with types. RAIRO-Theoretical Informatics and Applications, 38(4):277–319, 2004.
[2] Andreas Abel, Thierry Coquand, and Peter Dybjer. Verifying a semanticβη-conversion test for martin-löf type theory. In International Conference
on Mathematics of Program Construction, 2008.
[3] Andrea Asperti, Wilmer Ricciotti, Claudio Sacerdoti Coen, and Enrico
Tassi. A bi-directional refinement algorithm for the calculus of (co) inductive constructions. Logical Methods in Computer Science, 8:1–49, 2012.
[4] Luca Cardelli. An implementation of fsub. Technical report, Research
Report 97, Digital Equipment Corporation Systems Research Center, 1993.
[5] Gang Chen. Coercive subtyping for the calculus of constructions. POPL
’03, 2003.
[6] James Cheney and Ralf Hinze. First-class phantom types. Technical Report
CUCIS TR2003-1901, Cornell University, 2003.
[7] Adam Chlipala, Leaf Petersen, and Robert Harper. Strict bidirectional
type checking. In International Workshop on Types in Languages Design
and Implementation, 2005.
[8] Mario Coppo, Mariangiola Dezani-Ciancaglini, and Betti Venneri. Functional characters of solvable terms. Mathematical Logic Quarterly, 27(2-6):
45–58, 1981.
[9] Coq Development Team. The Coq proof assistant, 2015. Documentation,system download.
[10] Thierry Coquand. An algorithm for type-checking dependent types. Scienceof Computer Programming, 26(1-3):167–177, 1996.
[11] Luis Damas and Robin Milner. Principal type-schemes for functional programs. POPL ’82, 1982.
[12] Rowan Davies and Frank Pfenning. Intersection types and computational
effects. ICFP ’00, 2000.
[13] Joshua Dunfield. Greedy bidirectional polymorphism. In Workshop on ML,
2009.
[14] Joshua Dunfield and Neelakantan R. Krishnaswami. Complete and easy
bidirectional typechecking for higher-rank polymorphism. ICFP ’13, 2013.
[15] Joshua Dunfield and Frank Pfenning. Tridirectional typechecking. POPL
’04, 2004.
[16] Jean-Yves Girard. The system f of variable types, fifteen years later. Theoretical computer science, 45:159–192, 1986.
[17] J. Roger Hindley. The principal type-scheme of an object in combinatory
logic. Transactions of the American Mathematical Society, 146:29–60, 1969.
[18] Didier Le Botlan and Didier Rémy. Mlf: Raising ml to the power of system
f. ICFP ’03, 2003.
[19] Didier Le Botlan and Didier Rémy. Recasting mlf. Information and Computation, 207(6):726–785, 2009.

27[20] Daan Leijen. Flexible types: Robust type inference for first-class polymorphism. POPL ’09, 2009.
[21] Andres Löh, Conor McBride, and Wouter Swierstra. A tutorial implementation of a dependently typed lambda calculus. Fundamenta informaticae,
102(2):177–207, 2010.
[22] William Lovas. Refinement Types for Logical Frameworks. PhD thesis,
Carnegie Mellon University, 2010. AAI3456011.
[23] John C Mitchell. Polymorphic type inference and containment. Information
and Computation, 76(2-3):211–249, 1988.
[24] Martin Odersky and Konstantin Läufer. Putting type annotations to work.
POPL ’96, 1996.
[25] Martin Odersky, Christoph Zenger, and Matthias Zenger. Colored local
type inference. POPL ’01, 2001.
[26] Simon Peyton Jones, Dimitrios Vytiniotis, Stephanie Weirich, and Geoffrey
Washburn. Simple unification-based type inference for gadts. ICFP ’06,
2006.
[27] Simon Peyton Jones, Dimitrios Vytiniotis, Stephanie Weirich, and Mark
Shields. Practical type inference for arbitrary-rank types. Journal of functional programming, 17(01):1–82, 2007.
[28] Brigitte Pientka. A type-theoretic foundation for programming with higherorder abstract syntax and first-class substitutions. POPL ’08, 2008.
[29] Benjamin C Pierce and David N Turner. Local type inference. TOPLAS,
22(1):1–44, 2000.
[30] Garrel Pottinger. A type assignment for the strongly normalizable λ-terms.
To HB Curry: essays on combinatory logic, lambda calculus and formalism,
pages 561–577, 1980.
[31] Didier Rémy. Simple, partial type-inference for system f based on typecontainment. ICFP ’05, 2005.
[32] Didier Rémy and Boris Yakobowski. From ml to mlf: Graphic type constraints with efficient type inference. ICFP ’08, 2008.
[33] Tom Schrijvers, Simon Peyton Jones, Martin Sulzmann, and Dimitrios Vytiniotis. Complete and decidable type inference for gadts. ICFP ’09, 2009.
[34] Paula Severi and Erik Poll. Pure type systems with definitions. LogicalFoundations of Computer Science, pages 316–328, 1994.
[35] Dimitrios Vytiniotis, Stephanie Weirich, and Simon Peyton Jones. Fph:First-class polymorphism for haskell. ICFP ’08, 2008.
[36] Joe B Wells. Typability and type checking in system f are equivalent andundecidable. Annals of Pure and Applied Logic, 98(1-3):111–156, 1999.
[37] Hongwei Xi and Frank Pfenning. Dependent types in practical programming. POPL ’99, 1999.