Responding to referee comments

This commit is contained in:
ajeffrey@roblox.com 2021-09-15 16:46:24 -05:00
parent c738ddc1f0
commit 03e8edd37b
4 changed files with 141 additions and 69 deletions

View file

@ -128,3 +128,50 @@ number = {2},
journal = {ACM Trans. Program. Lang. Syst.}, journal = {ACM Trans. Program. Lang. Syst.},
pages = {109138}, pages = {109138},
} }
@InProceedings{Hazel,
author = {Cyrus Omar and Ian Voysey and Ravi Chugh and Matthew Hammer},
title = {Live Functional Programming with Typed Holes},
booktitle = {Proc. Symp. Principles of Programming Languages},
year = {2019},
pages = {14:1-14:28},
}
@InProceedings{MigratoryTyping,
author = {Sam Tobin-Hochstadt and Matthias Felleisen and Robert Bruce Findler and Matthew Flatt and Ben Greenman and Andrew M. Kent and Vincent St-Amour and T. Stephen Strickland and Asumu Takikawa},
title = {Migratory Typing: Ten Years Later},
booktitle = {Proc. Summit on Advances in Programming Languages},
year = {2017},
}
@InProceedings{LinkingTypes,
author = {Daniel Patterson and Amal Ahmed},
title = {Linking Types for Multi-Language Software: Have Your Cake and Eat It Too},
booktitle = {Proc. Summit on Advances in Programming Languages},
year = {2017},
}
@InProceedings{QuickLook,
author = {Serrano, Alejandro and Hage, Jurriaan and Peyton Jones, Simon and Vytiniotis, Dimitrios},
title = {A quick look at impredicativity},
booktitle = {Proc. Int. Conf. Functional Programming},
year = {2020},
}
@InProceedings{Boehm85,
author = {Partial polymorphic type inference is undecidable},
title = {Hans-J. Boehm},
booktitle = {Proc. Symp. Foundations of Computer Science},
year = {1985},
pages = {339-345},
}
@article{LocalTypeInference,
author = {Pierce, Benjamin C. and Turner, David N.},
title = {Local Type Inference},
year = {2000},
volume = {22},
number = {1},
journal = {ACM Trans. Program. Lang. Syst.},
pages = {144},
}

BIN
papers/hatra21/cc-by.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

View file

@ -5,6 +5,9 @@
\acmYear{2021} \acmYear{2021}
\acmConference[HATRA '21]{Human Aspects of Types and Reasoning Assistants}{October 2021}{Chicago, IL} \acmConference[HATRA '21]{Human Aspects of Types and Reasoning Assistants}{October 2021}{Chicago, IL}
\acmBooktitle{HATRA '21: Human Aspects of Types and Reasoning Assistants} \acmBooktitle{HATRA '21: Human Aspects of Types and Reasoning Assistants}
\acmDOI{}
\acmISBN{}
\expandafter\def\csname @copyrightpermission\endcsname{\raisebox{-1ex}{\includegraphics[height=3.5ex]{cc-by}} This work is licensed under a Creative Commons Attribution 4.0 International License.}
\usepackage{listings} \usepackage{listings}
@ -16,13 +19,18 @@
\newcommand{\TRUE}{\mathsf{true}} \newcommand{\TRUE}{\mathsf{true}}
\newcommand{\FALSE}{\mathsf{false}} \newcommand{\FALSE}{\mathsf{false}}
\newcommand{\NUMBER}{\mathsf{number}} \newcommand{\NUMBER}{\mathsf{number}}
\newcommand{\STRING}{\mathsf{string}}
\newcommand{\ERROR}{\mathsf{error}} \newcommand{\ERROR}{\mathsf{error}}
\newcommand{\IF}{\mathsf{if}\,} \newcommand{\IF}{\mathsf{if}\,}
\newcommand{\LOCAL}{\mathsf{local}\,}
\newcommand{\THEN}{\,\mathsf{then}\,} \newcommand{\THEN}{\,\mathsf{then}\,}
\newcommand{\ELSE}{\,\mathsf{else}\,} \newcommand{\ELSE}{\,\mathsf{else}\,}
\newcommand{\END}{\,\mathsf{end}} \newcommand{\END}{\,\mathsf{end}}
\newcommand{\FUNCTION}{\mathsf{function}\,} \newcommand{\FUNCTION}{\mathsf{function}\,}
\newcommand{\RETURN}{\mathsf{return}\,} \newcommand{\RETURN}{\mathsf{return}\,}
\newcommand{\FIND}{\mathsf{find}}
\newcommand{\PRINT}{\mathsf{print}}
\newcommand{\strlit}[1]{\mbox{``#1''}}
\begin{document} \begin{document}
@ -112,7 +120,7 @@ and multiplayer are all immediately accessible to creators.
\end{figure} \end{figure}
At some point during experience design, the experience creator has a need At some point during experience design, the experience creator has a need
that can't be met by the physics engine alone, such as ``the stairs should that can't be met by the game engine alone, such as ``the stairs should
light up when a player walks on them'' or ``a firework is set off light up when a player walks on them'' or ``a firework is set off
every few seconds.'' At this point, they will discover the script every few seconds.'' At this point, they will discover the script
editor, seen in Fig.~\ref{fig:studio}(b). editor, seen in Fig.~\ref{fig:studio}(b).
@ -123,6 +131,13 @@ editor, they have already built much of their creation, and have a
very specific concrete aim. As such, Luau must allow users to perform a very specific concrete aim. As such, Luau must allow users to perform a
specific task with as much help as possible from tools. specific task with as much help as possible from tools.
Type-driven tools are useful to all creators, in as much as they help
them achieve their current goals. For example type-driven
autocomplete, or type-driven API documentation, are of immediate
benefit. Traditional typechecking can be useful, for example for
catching spelling mistakes, but for most goal-driven developers, the
type system should help or get out of the way.
\subsection{Type-driven development} \subsection{Type-driven development}
Need: \emph{a language that supports large-scale codebases and defect detection} Need: \emph{a language that supports large-scale codebases and defect detection}
@ -166,7 +181,8 @@ even to creators who are not explicitly providing types.
Goal: \emph{provide type information even for ill-typed or syntactically invalid programs.} Goal: \emph{provide type information even for ill-typed or syntactically invalid programs.}
Programs spend much of their time under development in an ill-typed or incomplete state, even if the Programs spend much of their time under development in an ill-typed or incomplete state, even if the
final artifact is well-typed. Tools should support this by providing type information even for ill-typed final artifact is well-typed. If tools such as autocomplete and API documentation are type-driven,
this means that tooling needs to rely on type information even for ill-typed
or syntactically invalid programs. An analogy is infallible parsers, which perform error recovery and or syntactically invalid programs. An analogy is infallible parsers, which perform error recovery and
provide an AST for all input texts, even if they don't adhere to the parser's syntax. provide an AST for all input texts, even if they don't adhere to the parser's syntax.
@ -178,31 +194,26 @@ $\Gamma \vdash M \Rightarrow N : T$ where $N$ is an output term
where some subterms are \emph{flagged} as having type errors, written $\squnder{N}$. Write $\erase(N)$ where some subterms are \emph{flagged} as having type errors, written $\squnder{N}$. Write $\erase(N)$
for the result of erasing flaggings: $\erase(\squnder{N}) = \erase(N)$. for the result of erasing flaggings: $\erase(\squnder{N}) = \erase(N)$.
%% For example the usual For example, in Lua, the $\STRING.\FIND$ function expects two strings, and returns the
%% type rules for field access becomes: offsets for that string:
%% \[ \[
%% \infer{ \STRING.\FIND(\strlit{hello}, \strlit{ell}) \rightarrow (2, 4)
%% \Gamma \vdash M \Rightarrow M' : T \]
%% }{ and in Luau it has the type:
%% \Gamma \vdash M.\ell \Rightarrow M'.\ell : U \[
%% } \STRING.\FIND : (\STRING, \STRING) \rightarrow (\NUMBER?, \NUMBER?)
%% [ \]
%% T = \{ \overline{\ell:U} \} \mbox{ and } (\ell:U) \in (\overline{\ell:U}) In a conventional type system, there is no judgment for ill-typed terms
%% ] such as $\STRING.\FIND(\strlit{hello}, 37)$ but in an infallible system we flag the error
%% \] and approximate the type, for example:
%% but there is also a rule for unsuccessful field access: \[
%% \[ {} \vdash
%% \infer{ \STRING.\FIND(\strlit{hello}, 37)
%% \Gamma \vdash M \Rightarrow M' : T \Rightarrow
%% }{ \squnder{\STRING.\FIND(\strlit{hello}, 37)}
%% \Gamma \vdash M.\ell \Rightarrow \squnder{M'.\ell} : U :
%% } (\NUMBER?, \NUMBER?)
%% [ \]
%% T = \{ \overline{\ell:U} \} \mbox{ implies } \ell \not\in \overline{\ell}
%% ]
%% \]
%% In this type rule, $U$ is unconstrained.
The goal of infallible types is that every term can be typed: The goal of infallible types is that every term can be typed:
\begin{itemize} \begin{itemize}
\item \emph{Typability}: for every $M$ and $\Gamma$, \item \emph{Typability}: for every $M$ and $\Gamma$,
@ -224,8 +235,11 @@ there is a large body of work on type error reporting
(see, for example, the survey in~\cite[Ch.~3]{TopQuality}) (see, for example, the survey in~\cite[Ch.~3]{TopQuality})
and on type-directed program repair and on type-directed program repair
(see, for example, the survey in~\cite[Ch.~3]{RepairingTypeErrors}), (see, for example, the survey in~\cite[Ch.~3]{RepairingTypeErrors}),
but not type repair, or on but less on type repair.
the semantics of programs with type errors. Many compilers perform The closest work is Hazel's~\cite{Hazel} \emph{typed holes}
where $\squnder{N}$ is treated as a partially-filled hole in the program,
though in that work partially-filled holes are not erased at run-time.
Many compilers perform
error recovery during typechecking, but do not provide a semantics error recovery during typechecking, but do not provide a semantics
for programs with type errors. for programs with type errors.
@ -250,15 +264,34 @@ for any value $V$ we have $\squnder{V} \rightarrow V$, then show:
\item \emph{Progress}: if ${} \vdash M \Rightarrow N : T$, then either $N \rightarrow N'$ or $N$ is a value or $N$ has a flagged subterm. \item \emph{Progress}: if ${} \vdash M \Rightarrow N : T$, then either $N \rightarrow N'$ or $N$ is a value or $N$ has a flagged subterm.
\item \emph{Preservation}: if ${} \vdash M \Rightarrow N : T$ and $N \rightarrow N'$ then $M \rightarrow^*M'$ and ${} \vdash M' \Rightarrow N' : T$. \item \emph{Preservation}: if ${} \vdash M \Rightarrow N : T$ and $N \rightarrow N'$ then $M \rightarrow^*M'$ and ${} \vdash M' \Rightarrow N' : T$.
\end{itemize} \end{itemize}
Some issues raised by infallible types: For example in typechecking the program:
\[
\LOCAL (i,j) = \STRING.\FIND(x, y);
\IF i \THEN \PRINT(j-i) \END
\]
the interesting case is $i-j$ in a context where $i$ has type
$\NUMBER$ (since it is guarded by the $\IF$) but $j$ has type
$\NUMBER?$. Since subtraction has type $(\NUMBER, \NUMBER) \rightarrow \NUMBER$,
this is a type error, so we end up with
\[\begin{array}{r@{}l}
x: \STRING, y: \STRING \vdash {}&
(\LOCAL (i,j) = \STRING.\FIND(x, y);
\IF i \THEN \PRINT(j-i) \END) \\
\Rightarrow {}&
(\LOCAL (i,j) = \STRING.\FIND(x, y);
\IF i \THEN \PRINT(\squnder{j-i}) \END)
\end{array}\]
Some issues raised by soundness for infallible types:
\begin{itemize} \begin{itemize}
\item How should the judgments and their metatheory be set up? \item How should the judgments and their metatheory be set up?
\item How should type inference and generic functions be handled? \item How should type inference and generic functions be handled?
\item Is the operational semantics of flagged values \item Is the operational semantics of flagged values
($\squnder{V} \rightarrow V$) the right one? ($\squnder{V} \rightarrow V$) the right one?
\item Will higher-order code require wrappers on functions?
\end{itemize} \end{itemize}
\emph{Related work}: gradual typing and blame analysis, e.g.~\cite{GradualTyping,WellTyped,Contracts} \emph{Related work}: gradual typing and blame analysis, e.g.~\cite{GradualTyping,WellTyped,Contracts}.
The main difference between this approach and that of migratory typing~\cite{MigratoryTyping}
is that (due to backward compatibility with existing Lua) we cannot introduce
extra code during migration.
\subsection{Nonstrict types} \subsection{Nonstrict types}
@ -266,7 +299,7 @@ Goal: \emph{no false positives.}
For developers who are not interested in defect detection, type-driven For developers who are not interested in defect detection, type-driven
tools and techniques such as autocomplete, API documentation tools and techniques such as autocomplete, API documentation
and refactoring tools can still be useful. and type-driven refactoring are still useful.
For such developers, Luau provides a For such developers, Luau provides a
\emph{nonstrict mode}, which we hope will eventually be useful for all \emph{nonstrict mode}, which we hope will eventually be useful for all
developers. This non-strict typing mode is particularly useful when developers. This non-strict typing mode is particularly useful when
@ -276,7 +309,13 @@ soundness, but instead has the goal of ``no false positives``, in the
sense that any flagged code is guaranteed to produce a runtime error sense that any flagged code is guaranteed to produce a runtime error
when executed. when executed.
On the face of it, this is undecidable, since a program such as Our previous example was, in fact, a false positive since a programmer
can make use of the fact that $\STRING.\FIND(x, y)$ is either $\NIL$
in both results or neither, so if $i$ is non-$\NIL$ then so is $j$.
This is discussed in the English-language documentation but not reflected
in the type. So flagging $(i - j)$ is a false positive.
On the face of it, detecting errors without false positives is undecidable, since a program such as
$(\IF f() \THEN \ERROR \END)$ will produce a runtime error when $f()$ is $(\IF f() \THEN \ERROR \END)$ will produce a runtime error when $f()$ is
$\TRUE$, but we can aim for a weaker property: that all flagged code $\TRUE$, but we can aim for a weaker property: that all flagged code
is either dead code or will produce an error. Either of these is a is either dead code or will produce an error. Either of these is a
@ -296,10 +335,6 @@ Some issues raised by nonstrict types:
\item Under this definition, any function that will terminate is unflagged, so \item Under this definition, any function that will terminate is unflagged, so
flagging will often move from function definitions to call sites. flagging will often move from function definitions to call sites.
\item This definition will not allow an unchecked use of an optional value
to be flagged, for example if $f() : \NUMBER?$ (meaning $f$ may optionally return a number)
then a strict type system can flag $1 + f()$ but a nonstrict one cannot.
\item Property update of tables in languages like Luau always succeeds \item Property update of tables in languages like Luau always succeeds
(the property is inserted if it did not exist), and so functions which (the property is inserted if it did not exist), and so functions which
update properties cannot be flagged. update properties cannot be flagged.
@ -310,7 +345,7 @@ Some issues raised by nonstrict types:
\item The natural formulation of function types in a nonstrict setting \item The natural formulation of function types in a nonstrict setting
is that of~\cite{SuccessTyping}: if $f: T \rightarrow U$ and $f(V) \rightarrow^* W$ is that of~\cite{SuccessTyping}: if $f: T \rightarrow U$ and $f(V) \rightarrow^* W$
then $V:T$ and $W:U$. This formulation is \emph{covariant} in $T$, then $V:T$ and $W:U$. This formulation is \emph{covariant} in $T$,
not \emph{contavariant}; what impact does this have? not \emph{contravariant}; what impact does this have?
\end{itemize} \end{itemize}
\emph{Related work}: success types~\cite{SuccessTyping} and incorrectness logic~\cite{IncorrectnessLogic}. \emph{Related work}: success types~\cite{SuccessTyping} and incorrectness logic~\cite{IncorrectnessLogic}.
@ -343,40 +378,29 @@ Some questions raised by mixed-mode types:
\item Is strict-mode code sound when it relies on non-strict code, \item Is strict-mode code sound when it relies on non-strict code,
which has weaker invariants? which has weaker invariants?
\item How can we avoid introducing function wrappers in higher-order code
at the strict/nonstrict boundary?
\end{itemize} \end{itemize}
\emph{Related work}: this appears to be an under-explored area. \emph{Related work}: there has been work on interoperability between different type systems,
notably~\cite{LinkingTypes}, but there the overall goals of the systems were similar safety properties.
In our case, the two type systems have different goals.
\subsection{Type inference} \subsection{Type inference}
Goal: \emph{infer types to allow gradual adoption of type annotations.} Goal: \emph{infer types to allow gradual adoption of type annotations.}
Roblox, for many years, used the Lua language, which is dynamically typed and possesses a To make use of type-driven technologies for programs
very weak type system. Due to this large quantity of pre-existing dynamically-typed code, without explicit type annotations, we use a type inference algorithm.
it is essential for the type system to function even in the absence of type annotations, Since Luau includes System~F, type inference is undecidable~\cite{Boehm85},
in order to make the type features of Luau gradually adoptable. This means that Luau needs but we can still make use of heuristics such as local type inference~\cite{LocalTypeInference}.
to be able to infer types for symbols without any annotations being present, to the best of
its ability. This precludes syntactical rules such as Rust's requirement that all function
parameters be annotated with their type~\cite[Ch. 3.3]{RustBook}.
This requirement presents challenges for the type inference algorithm, because Luau may not have It remains to be seen if type inference can satisfy the goals of
enough information to determine the type of a given program. In non-strict mode in particular, strict and non-strict types. The current Luau system
which sees use in existing codebases, we cannot rely on the presence of type annotations. We also infers different types in the two modes, which is unsatisfactory as it
cannot require that the user provide them if Luau cannot deduce the type of a symbol. In cases makes changing mode a non-local breaking change. In addition,
such as this, we must admit defeat and assume that the code is correct, to fulfill non-strict non-strict inference is currently too imprecise to support
mode's goal of ``no false positives''. We do this by saying that the result of the operation is type-directed tools such as autocomplete.
$\mathsf{any}$, a type that can be converted to and from any other type freely.
In strict mode, Luau is not so limited, and in pursuit of the strict-mode goal of ``no false
negatives'', we may surface errors to the user indicating that the type inference system requires
more information, in the form of annotations, in order to type-check a piece of code. This code,
for example, requires a type annotation in order for Luau to determine the return type of the function (since Luau does not know if ``+'' refers to built-in addition on numbers, or a user-defined method):
\lstset{language=[5.1]Lua}
\begin{lstlisting}
function f(a, b)
return a + b
end
\end{lstlisting}
Some questions raised by type inference: Some questions raised by type inference:
\begin{itemize} \begin{itemize}
@ -384,10 +408,11 @@ Some questions raised by type inference:
\item How many cases in strict mode cannot be inferred by the type inference system? Minimizing \item How many cases in strict mode cannot be inferred by the type inference system? Minimizing
this kind of error is desirable, to make the type system as unobtrusive as possible. this kind of error is desirable, to make the type system as unobtrusive as possible.
\item Can something like the Rust traits system~\cite{RustBook} or Haskell classes~\cite{TypeClasses} be used to provide types for overloaded operators, without hopelessly confusing learners? \item Can something like the Rust traits system~\cite{RustBook} or Haskell classes~\cite{TypeClasses} be used to provide types for overloaded operators, without hopelessly confusing learners?
\item Type inference currently infers monotypes for unannotated
functions, in contrast to QuickLook~\cite{???}, which can infer generic types.
Will this be good enough for idiomatic Luau scripts?
\item Can type inference be used to infer the same types in strict and nonstrict mode, to ease migrating between modes, with the only difference being error reporting? \item Can type inference be used to infer the same types in strict and nonstrict mode, to ease migrating between modes, with the only difference being error reporting?
\end{itemize} \end{itemize}
\emph{Related work}: there is a large body of work on type inference, largely summarized in~\cite{TAPL}. \emph{Related work}: there is a large body of work on type inference, largely summarized in~\cite{TAPL}.
\section{Conclusions} \section{Conclusions}
@ -395,7 +420,7 @@ Some questions raised by type inference:
In this paper, we have presented some of the goals of the Luau type In this paper, we have presented some of the goals of the Luau type
system, and how they map to the needs of the Roblox creator system, and how they map to the needs of the Roblox creator
community. We have also explored how these goals differ from traditional community. We have also explored how these goals differ from traditional
type systems, where it is necessary to accomodate the unique needs of type systems, where it is necessary to accommodate the unique needs of
the Roblox platform. We have sketched what a solution might look like; the Roblox platform. We have sketched what a solution might look like;
all that remains is to draw the owl~\cite{HowToDrawAnOwl}. all that remains is to draw the owl~\cite{HowToDrawAnOwl}.