described in Section \ref{sec:refining}. Furthermore, our implementation uses for the inference of arrow types

...

...

@@ -17,16 +17,17 @@ The implementation is rather crude and consists of 2000 lines of OCaml code,

including parsing, type-checking of programs, and pretty printing of types. We

demonstrate the output of our type-checking implementation in

Table~\ref{tab:implem} and Table~\ref{tab:implem2}. Table~\ref{tab:implem} lists

some examples, none of which can be typed by current systems (even though some

some examples, none of which can be typed by current systems. Even though some

systems such as Flow and TypeScript can type some of these examples by adding

explicit type annotations, the code 6, 7, 9, and 10 in Table~\ref{tab:implem}

and, even more, the \code{and\_} and \code{xor\_} functions at the end of this

and, even more, the \code{and\_} and \code{xor\_} functions given

in \eqref{and+} and \eqref{xor+} later in this

section are out of reach of current systems, even when using the right explicit

annotations). Table~\ref{tab:implem2} allows for a direct comparison of with

annotations. Table~\ref{tab:implem2} allows for a direct comparison of with

\cite{THF10} be giving the type inferred for the fourteen examples given in that

work.

It should be noted that for all the examples we discuss, the the

It should be noted that for all the examples we present, the

time for the type inference process is less than 5ms, hence we do not report

precise timings in the table. These and other examples can be tested in the

\ifsubmission

...

...

@@ -38,16 +39,22 @@ online toplevel available at

\input{code_table}

In both tables, the second column gives a code fragment and the third

column the type deduced by our implementation. Code~1 is a

straightforward function similar to our introductory example \code{foo} in (\ref{foo},\ref{foo2}). Here the

column the type deduced by our implementation as is (we

pretty printed it but we did not alter the output). Code~1 is a

straightforward function similar to our introductory

example \code{foo} in (\ref{foo}) and (\ref{foo2}) where \code{incr}

is the successor function and \code{lneg} the logical negation for

Booleans. Here the

programmer annotates the parameter of the function with a coarse type

$\Int\vee\Bool$. Our implementation first type-checks the body of the

function under this assumption, but doing so it collects that the type of

$\texttt{x}$ is specialized to \Int{} in the ``then'' case and to \Bool{}

in the ``else'' case. The function is thus type-checked twice more

under each hypothesis for \texttt{x}, yielding the precise type

$(\Int\to\Int)\land(\Bool\to\Bool)$. Note that w.r.t.\ rule \Rule{AbsInf+} of Section~\ref{sec:refining}, our implementation improved the output of the computed

type. Indeed, using rule~[{\sc AbsInf}+] we would obtain the

$(\Int\to\Int)\land(\Bool\to\Bool)$. Note that w.r.t.\

rule \Rule{AbsInf+} of Section~\ref{sec:refining}, the

rule \Rule{AbsInf++} we use int the implementation improved the output of the computed

type. Indeed, using rule~[{\sc AbsInf}+] we would have obtained the

then the rule \Rule{OverApp} applies and \True, \Any, and $\lnot\True$ become candidate types for

\texttt{x}, which allows us to deduce the precise type given in the table. Finally, thanks to rule \Rule{OverApp} it is not necessary to use a type case to force refinement. As a consequence, we can define the functions \texttt{and\_} and \texttt{xor\_} more naturally as:

\begin{alltt}\color{darkblue}\morecompact

let and_ = fun (x : Any) -> fun (y : Any) -> not_ (or_ (not_ x) (not_ y))

let xor_ = fun (x : Any) -> fun (y : Any) -> and_ (or_ x y) (not_ (and_ x y))

\begin{alltt}\color{darkblue}

let and_ = fun (x : Any) -> fun (y : Any) -> not_ (or_ (not_ x) (not_ y))\refstepcounter{equation}\mbox{\color{black}\rm(\theequation)}\label{and+}

let xor_ = fun (x : Any) -> fun (y : Any) -> and_ (or_ x y) (not_ (and_ x y))\refstepcounter{equation}\mbox{\color{black}\rm(\theequation)}\label{xor+}

\end{alltt}

for which the very same types as in Table~\ref{tab:implem} are deduced.

...

...

@@ -167,7 +177,7 @@ explained, a single pass analysis would deduce

for {\tt x}

a type \Int{} from the {\tt f\;x} application and \Any{} from the {\tt g\;x}

application. Here by iterating a second time, the algorithm deduces

that {\tt x} has type $\Empty$ (i.e., $\textsf{Empty}$), that is that the first branch can never

that {\tt x} has type $\Empty$ (i.e., $\textsf{Empty}$), that is, that the first branch can never

be selected (and our implementation warns the user accordingly). In hindsight, the only way for a well-typed overloaded function to have

type $(\Int{\to}\Int)\land(\Any{\to}\Bool)$ is to diverge when the

argument is of type \Int: since this intersection type states that

...

...

@@ -175,7 +185,7 @@ whenever the input is \Int, {\em both\/} branches can be selected,

yielding a result that is at the same time an integer and a Boolean.

This is precisely reflected by the case $\Int\to\Empty$ in the result.

Indeed our {\tt example10} function can be applied to an integer, but

at runtime the application of {\tt f ~x} will diverge.

at runtime the application of {\tt f\,x} will diverge.

Code~11 simulates the behaviour of Javascript property resolution, by looking

for a property \texttt{l} either in the object \texttt{o} itself or in the

...

...

@@ -208,7 +218,7 @@ previously defined). Note that for all examples for which there was no explicit

indication in the original version, we \emph{infer} the type of the function.

Notice also that for Example~6, the goal of the example is to show that indeed,

the function is ill-typed (which our typechecker detects accurately). The

original Example~14 could be written in our syntax with :

original Example~14 could be written in our syntax as:

\begin{verbatim}

let example14_alt = fun (input : Int | String) ->

...

...

@@ -221,13 +231,13 @@ original Example~14 could be written in our syntax with :

\end{verbatim}

Notice, in the version above the absence of an occurrence of \texttt{input} in

the second \texttt{if} statement. Indeed, if the first test fails, it is either

because \texttt{is\_int (fst extra)} is not \texttt{True} (that is, if

because \texttt{is\_int (fst extra)} is not \texttt{True} (i.e., if

\texttt{fst extra} is not an integer) or because \texttt{input} is not an

integer. Therefore, in our setting, the type information propagated to the

second test is : $\texttt{(input, is\_int (fst extra))}\in\lnot(\Int,

\True)$, that is $\texttt{(input, is\_int (fst extra))}\in(\lnot\Int,

\True)\lor(\Int, \False)$ Therefore, the type of\texttt{input} in the second

branch is of type $(\String\lor\Int)\land(\lnot\Int\lor\int)=

\True)\lor(\Int, \False)$. Therefore, the type deduced for\texttt{input} in the second

branch is $(\String\lor\Int)\land(\lnot\Int\lor\Int)=

\String\lor\Int$ which is not precise enough. By adding an occurrence of

\texttt{input} in our \texttt{example14\_alt}, we can further restrict its type

typecheck the function. Lifting this limitation through a control-flow analysis

...

...

@@ -235,7 +245,7 @@ Notice, in the version above the absence of an occurrence of \texttt{input} in

Although these experiments are still preliminary, they show how the

combination occurrence typing and set-theoretic types, together

combination of occurrence typing and set-theoretic types, together

with the type inference for overloaded function types presented in

Section~\ref{sec:refining} goes beyond what languages like

TypeScript and Flow do, since they can only infer single arrow types.