In this work we presented to core of our analysis of occurrence
typing, extended it to record types and a proposed a couple of novel
applications of the theory, namely the inference of
intersection types for functions and a static analysis to reduce the number of
casts inserted when compiling gradually-typed programs.
There is still a lot of work to do to fill the gap with real-word
programming languages. Some of it should be quite routine such as the
encoding of specific language constructions (e.g., \code{isInt},
\code{typeof},...), the handling of more complex
kinds of checks (e.g., generic Boolean expression, multi-case
type-checks) and even encompass sophisticated type matching as the one
performed by the language CDuce. Some other will require more
work. For example, our analysis cannot handle flow of information. In
particular, the result of a type test can flow only to the branches
but not outside the test. As a consequence the current system cannot
type a let binding such as
\begin{alltt}\color{darkblue}
let x = (y\(\in\)Int)?`yes:`no in (x\(\in\)`yes)?y+1:not(y)
\end{alltt}
which is clearly safe when $y:\Int\vee\Bool$. Nor can this example be solved by partial evaluation since we do not handle nesting of tests in the condition\code{( ((y\(\in\)Int)?`yes:`no)\(\in\)`yes )? y+1 : not(y)},
and both are issues that system by~\citet{THF10} can handle. We think that it is possible
to reuse some of their ideas to perform a information flow analysis on the top of
our system to remove these limitations.
But the real challenges that lie ahead are the handling of side
effects and the addition of polymorphic types. Our analysis works in a
pure systems and extending it to cope with side-effects is not
immediate. We plan to do it by defining effect systems or by
performing some information flow analysis typically by enriching the
one we plan to develop for the limitations above. But our plan is not
more defined than that. For polymorphism, instead, we can easily adapt
the main idea of this work to the polymorphic setting. Indeed, the
main idea is to remove from the type of an expression all
the results of the expression that would make some test fail (or
succeed, if we are typing a negative branch). This is done by
applying an intersection to the type of the expression, so as to keep
only the values that may yield success (or failure) of the test. For
polymorphism the idea is the same, with the only difference that
besides applying an intersection we can also apply an
instantiation. The idea is to single out the two most general type
substitutions for which some test may succeed and fail, respectively, and apply these
substitutions to refine the types of the corresponding occurrences
in the ``then'' and ``else'' branches. Concretely, consider the test
$x_1x_2\in t$ where $t$ is a closed type and $x_1$, $x_2$ are
variables of type $x_1: s\to t$ and $x_2: u$ with $u\leq s$. For the
positive branch we first check whether there exists a type
substitution $\sigma$ such that $t\sigma\leq\neg\tau$. If it does not
exists, then this means that for all possible assignments of
polymorphic type variables of $s\to t$, the test may succeed, that is,
the success of the test does not depend on the particular instance of
$s\to t$ and, thus, it is not possible to pick some substitution for
refining the occurrence typing. If it exists, then
we find a type substitution $\sigma_\circ$ such that $\tau\leq
t\sigma_\circ$ and we refine the types of $x_1$ and $x_2$ for the
positive branch by applying $\sigma_\circ$ to their types. While the
idea is clear (see Appendix~\ref{app:roadmap} for a more detailed explanation),
the technical details are quite involved, especially when considering
functions typed by intersection types and/or when integrating gradual
typing. This deserves a whole pan of non trivial research that we plan to
Note that $\tsint\ts$ is closed under subsumption and intersection (straightforward induction).
\end{definition}
\begin{definition}[Representative]
We define a function $\tsrep{\_}$ that maps every non-empty type scheme into a type, \textit{representative} of the set of types denoted by the scheme.
Let $\ts$ be a type scheme and $t$ a type. We can compute a type scheme, written $t \tsand\ts$, such that:
\[\tsint{t \tsand\ts}=\{s \alt\exists t' \in\tsint\ts.\ t \land t' \leq s \}\]
\end{lemma}
\begin{lemma}
Let $\ts$ be a type scheme and $t$ a type. We can decide the assertion $t \in\tsint\ts$,
which we also write $\ts\leq t$.
\end{lemma}
\subsection{Expressions}
\[
\begin{array}{lrcl}
\textbf{Expressions}& e & ::=& c\alt x \alt\lambda^{\bigwedge\arrow t t}x.e \alt\ite k t e e \alt e e\\
\textbf{Conditions}& k & ::=& c\alt x \alt\lambda^{\bigwedge\arrow t t}x.e \alt k k\\
\textbf{Values}& v & ::=& c \alt\lambda^{\bigwedge\arrow t t}x.e\\
\end{array}
\]
Let $e$ be an expression and $\varpi\in\{0,1\}^*$ a \emph{path}; we denote $\occ e\varpi$ the occurrence of $e$ reached by the path $\varpi$, that is
\[
\begin{array}{r@{\downarrow}l@{\quad=\quad}l}
e&\epsilon& e\\
e_0e_1& i.\varpi&\occ{e_i}\varpi\qquad i=0,1\\
\end{array}
\]
undefined otherwise
A type environment $\Gamma$ is a mapping from occurrences (i.e., expressions) to types, up to alpha-renaming (i.e., $\lambda^tx.x$ and $\lambda^ty.y$ are mapped to the same type, if any).
It is necessary to map alpha-equivalent expressions to the same type in order for our type system to be invariant by alpha-renaming.
We use $\Gamma_1,\Gamma_2$ for the type environment obtained by unioning the two type environments giving priority to $\Gamma_2$ (define formally).
We suppose w.l.o.g that all variables abstracted in $\lambda$-abstractions are distincts (otherwise we can alpha-rename $\lambda$-abstractions).