one that states that \code{foo} returns a number when it is applied to
a number and it returns a string when it is applied to a string, so that the type deduced for, say \code{foo(42)} would be \code{number} rather than \code{number|string}. This is
exactly what the intersection type \code{(number$\to$number) \&
(string$\to$string)} states (intuitively, an expression has an intersection of types if and only if it has all the types of the intersection) and corresponds in Flow to declaring \code{foo} as follows:
(string$\to$string)} states (intuitively, an expression has an intersection of type, noted \code{\&}, if and only if it has all the types of the intersection) and corresponds in Flow to declaring \code{foo} as follows:
\begin{alltt}\color{darkblue}
var foo : \textcolor{darkred}{(number => number) & (string => string)} = x => \{
@@ -71,8 +71,8 @@ fails. We already saw them at work when we informally typed the ``else''
branch in \code{foo}, for which we assumed that $x$ did \emph{not} have
type \code{number}---i.e., it had the (negation) type \code{$\neg$number}---and
deduced from it that $x$ then had type \code{string}---i.e.,
\code{(number|string)\&$\neg$number}, that is,
\code{(number|string)\textbackslash\,number}which is equivalent to \code{string}.
\code{(number|string)\&$\neg$number} which is equivalent to the set-theoretic difference
\code{(number|string)\textbackslash\,number}and, thus, to \code{string}.
The approaches described above essentially focus on refining the type
of variables that occur in an expression whose type is being tested. They
...
...
@@ -106,7 +106,7 @@ We focus our study on conditionals that test types and consider the following s
\(
\ifty{x}{\Int}{x+1}{(\textsf{trim } x)}
\)).
In particular, in this introduction section we concentrate on applications, since they constitute the most difficult case and many other cases can be reduced to it. A typical example is the expression
In particular, in this introduction section we concentrate on applications, since they constitute the most difficult case and many other cases can be reduced to them. A typical example is the expression
\begin{equation}\label{typical}
\ifty{x_1x_2}{t}{e_1}{e_2}
\end{equation}
...
...
@@ -114,7 +114,7 @@ where $x_i$'s denote variables, $t$ is some type, and $e_i$'s are generic expres
Depending the actual $t$ and the static types of $x_1$ and $x_2$ we
can make type assumptions for $x_1$, for $x_2$\emph{and} for the application $x_1x_2$
when typing $e_1$ that are different from those we can make when typing
$e_2$. For instance, suppose $x_1$ is bound to the function \code{foo} defined in \eqref{foo2}. Thus $x_1$ has type, in the syntax of our formalism, $(\Int\to\Int)\wedge(\String\to\String)$.
$e_2$. For instance, suppose $x_1$ is bound to the function \code{foo} defined in \eqref{foo2}. Thus $x_1$ has type, $(\Int\to\Int)\wedge(\String\to\String)$ (we used the syntax of the types of Section~\ref{sec:language} where unions and intersections are denoted by $\vee$ and $\wedge$).
Then it is not hard to see that the expression\footnote{Most of the following expressions are given just for the sake of example. Determining the type of expressions other than variables is interesting for constructor expressions but less so for destructors such as applications, projections, and selections: any reasonable programmer would not repeat the same application twice, it would store its result in a variable. This becomes meaningful when we introduce constructor such as pairs, as we do for instance in the expression in~\eqref{pair}.}
%
\begin{equation}\label{mezzo}
...
...
@@ -210,18 +210,18 @@ well-typed lambda-abstractions, the values of functional type) whose domain, den
$t_1$; second that $t_2$ must be a subtype of the domain of $t_1$;
third, we also know the type of the application, that is the type
that denotes all the values that may result from the application of a
function in $t_1$ to an argument in $t_2$ and that we denote by
function in $t_1$ to an argument in $t_2$, type that we denote by
$t_1\circ t_2$. For instance, if $t_1=\Int\to\Bool$ and $t_2=\Int$,
then $\dom{t_1}=\Int$ and $t_1\circ t_2=\Bool$. Notice that, introducing operations such as $\dom{}$ and $\circ$ is redundant when working with simple types, but becomes necessary in the presence of set-theoretic types. If for instance \(t_1=(\Int\to\Int)\)\(\wedge\)\((\Bool\to\Bool)\)---which
denotes functions that will return an integer if applied to an integer and a Boolean if applied to a Boolean---, then we have
\(\dom{t}=\Int\vee\Bool\),
then $\dom{t_1}=\Int$ and $t_1\circ t_2=\Bool$. Notice that, introducing operations such as $\dom{}$ and $\circ$ is redundant when working with simple types, but becomes necessary in the presence of set-theoretic types. If for instance $t_1$ is the type of \eqref{foo2}, that is, \(t_1=(\Int{\to}\Int)\)\(\wedge\)\((\String{\to}\String)\)---which
denotes functions that return an integer if applied to an integer and a string if applied to a string---, then
\(\dom{t}=\Int\vee\String\),
that is the union of all the possible
input types. Now the precise return type of such a
function depends on what the type of the argument of such a function is:
input types, while the precise return type of such a
function depends on the type of the argument the function is applied to:
either an integer, or a Boolean, or both (i.e., the argument has union type
\(\Int\vee\Bool\)). So we have \( t \circ\Int=\Int\),
\( t_1\circ\Bool=\Bool\), and
\( t_1\circ(\Int\vee\Bool)=\Int\vee\Bool\) (see Section~\ref{sec:typeops} for the formal definition of $\circ$).
\(\Int\vee\String\)). So we have \( t_1\circ\Int=\Int\),
\( t_1\circ\String=\String\), and
\( t_1\circ(\Int\vee\String)=\Int\vee\String\) (see Section~\ref{sec:typeops} for the formal definition of $\circ$).
What we want to do
is to refine the types of $e_1$ and $e_2$ (i.e., $t_1$ and $t_2$) for the cases where the test
...
...
@@ -239,7 +239,7 @@ $t_2$ all the values in $s$ or, equivalently, keep in $t_2$ all the
values of $\dom{t_1}$ that are not in $s$. Let us implement the second
viewpoint: the set of all elements of $\dom{t_1}$ for which an
application \emph{does not} surely give a result in $\neg t$ is
denoted $\worra{t_1}t$ and defined as $\min\{u\leq\dom{t_1}\alt
denoted $\worra{t_1}t$(read, ``worra'') and defined as $\min\{u\leq\dom{t_1}\alt
t_1\circ(\dom{t_1}\setminus u)\leq\neg t\}$: it is easy to see that
according to this definition $\dom{t_1}\setminus(\worra{t_1} t)$ is
the largest subset of $\dom{t_1}$ satisfying \eqref{eq1}. Then we can
...
...
@@ -292,7 +292,7 @@ obtain
and since
$(\Bool{\vee}\String\to\Bool)\wedge\neg(\String\to\neg\Int)$ is empty
then what we obtain is a strict subtype of $\Int{\vee}\String\to\Int$, namely the functions of type $\Int{\vee}\String\to\Int$ minus those
then what we obtain is the left summand, a strict subtype of $\Int{\vee}\String\to\Int$, namely the functions of type $\Int{\vee}\String\to\Int$ minus those
that diverge on all \String{} arguments.
...
...
@@ -407,5 +407,5 @@ retyping the expression without that assumption (see rule
\subsubsection*{Outline} Our presentation proceeds as follows
For space reasons several technical definitions and all the proofs are omitted from this presentation and can be found in the Appendix available online.
For space reasons several technical definitions and all the proofs are omitted from this presentation and can be found in the appendix available online.