Clarify that global/local/static functions can also be closurized
This CL changes dartLangSpec.tex to say that 'closurization' takes place (rather than just saying that it's a 'function') when a global/local/static function is torn off, just like it always did for instance methods. Also, it standardizes on using the phrase 'function object' to denote the run-time entity obtained from such a closurization. This addresses the non-generic parts of the request in #31665. Change-Id: I6967a74df178fbb26af0f572b0471219d3169e4f Reviewed-on: https://dart-review.googlesource.com/46860 Commit-Queue: Erik Ernst <eernst@google.com> Reviewed-by: Lasse R.H. Nielsen <lrn@google.com> Reviewed-by: Leaf Petersen <leafp@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
64754764b1
commit
543d8775e3
@@ -1,5 +1,7 @@
|
||||
dartLangSpec.aux
|
||||
dartLangSpec.log
|
||||
dartLangSpec.out
|
||||
dartLangSpec.pdf
|
||||
dartLangSpec.toc
|
||||
dartLangSpec*.aux
|
||||
dartLangSpec*.log
|
||||
dartLangSpec*.out
|
||||
dartLangSpec*.pdf
|
||||
dartLangSpec*.toc
|
||||
*-hash.tex
|
||||
*-list.txt
|
||||
|
||||
+102
-33
@@ -43,6 +43,8 @@
|
||||
% - Update mixin application forwarding constructors to correctly handle
|
||||
% optional parameters and const constructors.
|
||||
% - Specify `call` for Dart 2 (no function type given to enclosing class).
|
||||
% - Clarify that an identifier reference denoting a top-level, static, or
|
||||
% local function evaluates to the closurization of that declaration.
|
||||
%
|
||||
% 1.15
|
||||
% - Change how language specification describes control flow.
|
||||
@@ -1128,11 +1130,11 @@ One cannot assume, based on the above, that given a function \code{f}, \code{f.r
|
||||
}
|
||||
|
||||
\rationale{
|
||||
It is up to the implementation to choose an appropriate representation for functions.
|
||||
For example, consider that a closure produced via property extraction treats equality different from ordinary closures, and is therefore likely a different class.
|
||||
Implementations may also use different classes for functions based on arity and or type.
|
||||
It is up to the implementation to choose an appropriate representation for function objects.
|
||||
For example, consider that a function object produced via property extraction treats equality differently from other function objects, and is therefore likely a different class.
|
||||
Implementations may also use different classes for function objects based on arity and or type.
|
||||
Arity may be implicitly affected by whether a function is an instance method (with an implicit receiver parameter) or not.
|
||||
The variations are manifold, and so this specification only guarantees that function objects are instances of some class that is considered to implement \FUNCTION{}.
|
||||
The variations are manifold, and so this specification only guarantees that function objects are instances of some class that implements \FUNCTION{}.
|
||||
}
|
||||
|
||||
|
||||
@@ -3871,6 +3873,8 @@ The static type of a throw expression is $\bot$.
|
||||
\LMLabel{functionExpressions}
|
||||
|
||||
\LMHash{}
|
||||
%% TODO(eernst): A function literal is a syntactic construct, and we may use
|
||||
%% function closurization to obtain a corresponding function object.
|
||||
A {\em function literal} is an object that encapsulates an executable unit of code.
|
||||
|
||||
\begin{grammar}
|
||||
@@ -4915,6 +4919,45 @@ Otherwise, the static type of $i$ is the return type
|
||||
$[A_1/X_1, \ldots, A_r/X_s]T_0$ of $F$.
|
||||
|
||||
|
||||
\subsection{Function Closurization}
|
||||
\LMLabel{functionClosurization}
|
||||
|
||||
\LMHash{}
|
||||
Let $f$ be an expression denoting
|
||||
a declaration of a local function, a static method, or a top-level function
|
||||
(\ref{identifierReference})
|
||||
or let $f$ be a function literal
|
||||
(\ref{functionExpressions}).
|
||||
Evaluation of $f$ yields a function object
|
||||
which is the outcome of a {\em function closurization}
|
||||
applied to the declaration denoted by $f$
|
||||
respectively to the function literal $f$ considered as a function declaration.
|
||||
% Note that we do not promise that this will be a fresh function object,
|
||||
% such that constant expressions are covered as well, and others may be
|
||||
% identical even if not required, e.g., local functions with no free variables.
|
||||
{\em Closurization} denotes instance method closurization
|
||||
(\ref{ordinaryMemberClosurization})
|
||||
as well as function closurization,
|
||||
and it is also used as a shorthand for either of them when there is no ambiguity.
|
||||
|
||||
\LMHash{}
|
||||
Function closurization applied to a function declaration $f$
|
||||
amounts to the creation of a function object $o$
|
||||
which is an instance of a class whose interface is a subtype of the actual type
|
||||
(\ref{actualTypeOfADeclaration})
|
||||
corresponding to the signature in the function declaration $f$,
|
||||
using the current bindings of type variables, if any.
|
||||
%
|
||||
An invocation of $o$ with a given argument list will bind actuals to formals
|
||||
in the same way as an invocation of $f$
|
||||
(\ref{bindingActualsToFormals}),
|
||||
and then execute the body of $f$
|
||||
in the captured scope amended with the bound parameter scope,
|
||||
yielding the same completion
|
||||
(\ref{completion})
|
||||
as the invocation of $f$ would have yielded.
|
||||
|
||||
|
||||
\subsection{Lookup}
|
||||
\LMLabel{lookup}
|
||||
|
||||
@@ -5380,16 +5423,19 @@ In other words, the methods supporting sending messages embody primitives of Dar
|
||||
\LMLabel{propertyExtraction}
|
||||
|
||||
\LMHash{}
|
||||
{\em Property extraction} allows for a member or constructor to be accessed as a property rather than a function.
|
||||
{\em Property extraction} allows for a member to be accessed as a property rather than a function.
|
||||
A property extraction can be either:
|
||||
\begin{enumerate}
|
||||
\item A {\em closurization} which converts a method or constructor into a closure.
|
||||
\item An instance method closurization,
|
||||
which converts a method into a function object
|
||||
(\ref{ordinaryMemberClosurization}).
|
||||
Or
|
||||
\item A {\em getter invocation} which returns the result of invoking of a getter method.
|
||||
\item A getter invocation, which returns the result of invoking of a getter method
|
||||
(\ref{getterAccessAndMethodExtraction}).
|
||||
\end{enumerate}
|
||||
|
||||
\commentary{
|
||||
Closures derived from members via closurization are colloquially known as tear-offs.
|
||||
Function objects derived via closurization are colloquially known as tear-offs.
|
||||
}
|
||||
|
||||
Property extraction can be either {\em conditional} or {\em unconditional}.
|
||||
@@ -5560,9 +5606,15 @@ Note that the non-generic case is covered implicitly using $s = 0$,
|
||||
in which case the type parameter declarations are omitted (\ref{generics}).
|
||||
}
|
||||
|
||||
\LMHash{}
|
||||
An {\em instance method closurization}
|
||||
is a closurization of some method on some object, defined below,
|
||||
or a super closurization (\ref{superClosurization}).
|
||||
|
||||
\LMHash{}
|
||||
Let $o$ be an object, and let $u$ be a fresh final variable bound to $o$.
|
||||
The {\em closurization of method $f$ on object $o$} is defined to be equivalent to:
|
||||
The {\em closurization of method $f$ on object $o$} is defined to be equivalent
|
||||
(\commentary{except for equality, as noted below}) to:
|
||||
\begin{itemize}
|
||||
%\item $(a) \{\RETURN{}$ $u$ $op$ $a;$\} if $f$ is named $op$ and $op$ is one of \code{<, >, <=, >=, ==, -, +, /, \~{}/, *, \%, $|$, \^{}, \&, $<<$, $>>$} (this precludes closurization of unary -).
|
||||
%\item $() \{\RETURN{}$ \~{} $u;$\} if $f$ is named \~{}.
|
||||
@@ -5638,8 +5690,13 @@ where $S_j$ is the type annotation of the corresponding parameter in $D$.
|
||||
If that parameter declaration has no type annotation then $T_j$ is \DYNAMIC{}.
|
||||
|
||||
\LMHash{}
|
||||
There is one way in which the closurization differs from the function literal:
|
||||
\code{$o_1.m$ == $o_2.m$} is equal to \code{identical($o_1, o_2$)}.
|
||||
There is one way in which
|
||||
the function object yielded by the instance method closurization differs from
|
||||
the function object obtained by function closurization on the above mentioned function literal:
|
||||
Assume that $o_1$ and $o_2$ are objects, $m$ is an identifier,
|
||||
and $c_1$ and $c_2$ are function objects
|
||||
obtained by closurization of $m$ on $o_1$ respectively $o_2$.
|
||||
Then \code{$c_1$ == $c_2$} evaluates to true if and only if $o_1$ and $o_2$ is the same object.
|
||||
|
||||
%\item The static type of the property extraction is the static type of function $T.m$, where $T$ is the static type of $e$, if $T.m$ is defined. Otherwise the static type of $e.m$ is \DYNAMIC{}.
|
||||
|
||||
@@ -5648,12 +5705,12 @@ There is one way in which the closurization differs from the function literal:
|
||||
% and for the closurizations.
|
||||
In particular, two closurizations of a method $m$ from the same object are equal,
|
||||
and two closurizations of a method $m$ from non-identical objects are not equal.
|
||||
It also follows that \code{identical($o_1.m, o_2.m$)} must be false when $o_1$ and $o_2$ are not identical.
|
||||
However, Dart implementations are not required to canonicalize closures,
|
||||
which means that \code{identical($o_1.m, o_2.m$)} is not guaranteed to be true,
|
||||
even when it is known that $o_1$ and $o_2$ are identical.
|
||||
Assuming that $v_i$ is a fresh variable bound to an object, $i \in 1 .. 2$,
|
||||
it also follows that \code{identical($v_1.m, v_2.m$)} must be false when $v_1$ and $v_2$ are not bound to the same object.
|
||||
However, Dart implementations are not required to canonicalize function objects,
|
||||
which means that \code{identical($v_1.m, v_2.m$)} is not guaranteed to be true,
|
||||
even when it is known that $v_1$ and $v_2$ are bound to the same object.
|
||||
}
|
||||
% local functions that have a closure extracted are always different
|
||||
|
||||
\rationale{
|
||||
The special treatment of equality in this case facilitates the use of extracted property functions in APIs where callbacks such as event listeners must often be registered and later unregistered.
|
||||
@@ -5679,7 +5736,11 @@ In short, consider a situation where a superinvocation of $f$ will execute $f$ a
|
||||
}
|
||||
|
||||
\LMHash{}
|
||||
The {\em closurization of method $f$ with respect to $S$} is defined to be equivalent to:
|
||||
A {\em super closurization}
|
||||
is a closurization of a method with respect to a class, as defined next.
|
||||
The {\em closurization of the method $f$ with respect to the class $S$}
|
||||
is defined to be equivalent
|
||||
(\commentary{except for equality, as noted below}) to:
|
||||
|
||||
\LMHash{}
|
||||
\begin{itemize}
|
||||
@@ -5711,6 +5772,11 @@ required parameters $r_1, \ldots, r_n$,
|
||||
and optional positional parameters $p_1, \ldots, p_k$ with defaults $d_1, \ldots, d_k$.
|
||||
\end{itemize}
|
||||
|
||||
\commentary{
|
||||
Note that a super closurization is an {\em instance method closurization},
|
||||
as defined in (\ref{ordinaryMemberClosurization}).
|
||||
}
|
||||
|
||||
\LMHash{}
|
||||
$B'_j, j \in 1 .. s$, are determined as follows:
|
||||
If $S$ is a non-generic class then $B'_j = B_j, j \in 1 .. s$.
|
||||
@@ -5752,10 +5818,13 @@ where $S_j$ is the type annotation of the corresponding parameter in $D$.
|
||||
If that parameter declaration has no type annotation then $T_j$ is \DYNAMIC{}.
|
||||
|
||||
\LMHash{}
|
||||
There is one way in which the closurization differs from the function literal:
|
||||
Assume that an occurrence of the expression \SUPER{}.$m$ in a given class is evaluated on two occasions where \THIS{} is $o_1$ respectively $o_2$,
|
||||
and the resulting closurization is $c_1$ respectively $c_2$:
|
||||
\code{$c_1$ == $c_2$} is then equal to \code{identical($o_1, o_2$)}.
|
||||
There is one way in which
|
||||
the function object yielded by the super closurization differs from
|
||||
the function object obtained by function closurization on the above mentioned function literal:
|
||||
Assume that an occurrence of the expression \SUPER{}.$m$ in a given class
|
||||
is evaluated on two occasions where \THIS{} is bound to $o_1$ respectively $o_2$,
|
||||
and the resulting function objects are $c_1$ respectively $c_2$:
|
||||
\code{$c_1$ == $c_2$} is then true if and only if $o_1$ and $o_2$ is the same object.
|
||||
|
||||
|
||||
\subsection{Assignment}
|
||||
@@ -6121,8 +6190,8 @@ Otherwise the value of $c$ is the result of evaluating the expression $e_3$.
|
||||
If all of the following hold:
|
||||
\begin{itemize}
|
||||
\item $e_1$ shows that a variable $v$ has type $T$.
|
||||
\item $v$ is not potentially mutated in $e_2$ or within a closure.
|
||||
\item If the variable $v$ is accessed by a closure in $e_2$ then the variable $v$ is not potentially mutated anywhere in the scope of $v$.
|
||||
\item $v$ is not potentially mutated in $e_2$ or within a function.
|
||||
\item If the variable $v$ is accessed by a function in $e_2$ then the variable $v$ is not potentially mutated anywhere in the scope of $v$.
|
||||
\end{itemize}
|
||||
|
||||
then the type of $v$ is known to be $T$ in $e_2$.
|
||||
@@ -6186,15 +6255,15 @@ $T$ if all of the following conditions hold:
|
||||
\begin{itemize}
|
||||
\item Either $e_1$ shows that $v$ has type $T$ or $e_2$ shows that $v$ has type $T$.
|
||||
\item $v$ is a local variable or formal parameter.
|
||||
\item The variable $v$ is not mutated in $e_2$ or within a closure.
|
||||
\item The variable $v$ is not mutated in $e_2$ or within a function.
|
||||
\end{itemize}
|
||||
|
||||
\LMHash{}
|
||||
Furthermore, if all of the following hold:
|
||||
\begin{itemize}
|
||||
\item $e_1$ shows that $v$ has type $T$.
|
||||
\item $v$ is not mutated in either $e_1$, $e_2$ or within a closure.
|
||||
\item If the variable $v$ is accessed by a closure in $e_2$ then the variable $v$ is not potentially mutated anywhere in the scope of $v$.
|
||||
\item $v$ is not mutated in either $e_1$, $e_2$ or within a function.
|
||||
\item If the variable $v$ is accessed by a function in $e_2$ then the variable $v$ is not potentially mutated anywhere in the scope of $v$.
|
||||
\end{itemize}
|
||||
then the type of $v$ is known to be $T$ in $e_2$.
|
||||
|
||||
@@ -6849,7 +6918,7 @@ If, however, $e$ occurs inside a static member, a compile-time error occurs.
|
||||
% \end{itemize}
|
||||
\item If $d$ is a local variable or formal parameter then $e$ evaluates to the current binding of $id$.
|
||||
%\item If $d$ is a library variable, local variable, or formal parameter, then $e$ evaluates to the current binding of $id$. \commentary{This case also applies if d is a library or local function declaration, as these are equivalent to function-valued variable declarations.}
|
||||
\item If $d$ is a static method, top-level function or local function then $e$ evaluates to the function defined by $d$.
|
||||
\item If $d$ is a static method, top-level function or local function then $e$ evaluates to the function object obtained by closurization (\ref{functionClosurization}) of the declaration denoted by $d$.
|
||||
\item If $d$ is the declaration of a static variable, static getter or static setter declared in class $C$, then evaluation of $e$ is equivalent to evaluation of the property extraction (\ref{propertyExtraction}) $C.id$.
|
||||
\item If $d$ is the declaration of a library variable, top-level getter or top-level setter, then evaluation of $e$ is equivalent to evaluation of the top level getter invocation (\ref{topLevelGetterInvocation}) $id$.
|
||||
\item Otherwise, if $e$ occurs inside a top level or static function (be it function, method, getter, or setter) or variable initializer, evaluation of $e$ causes a \code{NoSuchMethod} to be thrown.
|
||||
@@ -7155,7 +7224,7 @@ top() \{ // another top level function
|
||||
|
||||
\commentary{
|
||||
There is no way to write a pair of mutually recursive local functions, because one always has to come before the other is declared.
|
||||
These cases are quite rare, and can always be managed by defining a pair of variables first, then assigning them appropriate closures:
|
||||
These cases are quite rare, and can always be managed by defining a pair of variables first, then assigning them appropriate function literals:
|
||||
}
|
||||
|
||||
\begin{dartCode}
|
||||
@@ -7172,8 +7241,6 @@ This is because recursive functions are useful whereas recursively defined varia
|
||||
It therefore makes sense to harmonize the rules for local functions with those for functions in general rather than with the rules for local variables.
|
||||
}
|
||||
|
||||
% elaborate on function identity and equality, run-time type. Likewsie in function expressions (closures) and declarations
|
||||
|
||||
|
||||
\subsection{If}
|
||||
\LMLabel{if}
|
||||
@@ -7225,8 +7292,8 @@ It is a static type warning if the type of the expression $b$ may not be assigne
|
||||
If:
|
||||
\begin{itemize}
|
||||
\item $b$ shows that a variable $v$ has type $T$.
|
||||
\item $v$ is not potentially mutated in $s_1$ or within a closure.
|
||||
\item If the variable $v$ is accessed by a closure in $s_1$ then the variable $v$ is not potentially mutated anywhere in the scope of $v$.
|
||||
\item $v$ is not potentially mutated in $s_1$ or within a function.
|
||||
\item If the variable $v$ is accessed by a function in $s_1$ then the variable $v$ is not potentially mutated anywhere in the scope of $v$.
|
||||
\end{itemize}
|
||||
then the type of $v$ is known to be $T$ in $s_1$.
|
||||
|
||||
@@ -7295,7 +7362,9 @@ The expression $[v''/v]e$ is evaluated, and the process recurses at step \ref{be
|
||||
\end{enumerate}
|
||||
|
||||
\rationale{
|
||||
The definition above is intended to prevent the common error where users create a closure inside a for loop, intending to close over the current binding of the loop variable, and find (usually after a painful process of debugging and learning) that all the created closures have captured the same value - the one current in the last iteration executed.
|
||||
The definition above is intended to prevent the common error where users create a function object inside a for loop,
|
||||
intending to close over the current binding of the loop variable,
|
||||
and find (usually after a painful process of debugging and learning) that all the created function objects have captured the same value---the one current in the last iteration executed.
|
||||
|
||||
Instead, each iteration has its own distinct variable.
|
||||
The first iteration uses the variable created by the initial declaration.
|
||||
|
||||
Reference in New Issue
Block a user