intro
This commit is contained in:
parent
8b0e77cf50
commit
7e1ef7b3c1
142
introduction.tex
142
introduction.tex
@ -387,6 +387,10 @@ let l1' : (*@$\wctype{\rwildcard{X}}{List}{\exptype{List}{\rwildcard{X}}}$@*) =
|
|||||||
\section{Global Type Inference Algorithm}
|
\section{Global Type Inference Algorithm}
|
||||||
\label{sec:glob-type-infer}
|
\label{sec:glob-type-infer}
|
||||||
|
|
||||||
|
This section gives an overview of the global type inference algorithm
|
||||||
|
with examples and identifies the challenges that we had to overcome in
|
||||||
|
the design of the algorithm.
|
||||||
|
|
||||||
\begin{figure}[h]
|
\begin{figure}[h]
|
||||||
\begin{minipage}{0.49\textwidth}
|
\begin{minipage}{0.49\textwidth}
|
||||||
\begin{lstlisting}[style=tfgj, caption=Valid Java program, label=lst:addExample]
|
\begin{lstlisting}[style=tfgj, caption=Valid Java program, label=lst:addExample]
|
||||||
@ -428,25 +432,30 @@ let l2:(*@$\wctype{\wildcard{X}{\type{Object}}{\type{String}}}{List}{\rwildcard{
|
|||||||
% \end{description}
|
% \end{description}
|
||||||
|
|
||||||
%Our algorithm is an extension of the \emph{Global Type Inference for Featherweight Generic Java}\cite{TIforFGJ} algorithm.
|
%Our algorithm is an extension of the \emph{Global Type Inference for Featherweight Generic Java}\cite{TIforFGJ} algorithm.
|
||||||
Listings \ref{lst:addExample}, \ref{lst:addExampleLet}, \ref{lst:addExampleCons}, and
|
Listings~\ref{lst:addExample}, \ref{lst:addExampleLet}, \ref{lst:addExampleCons}, and
|
||||||
\ref{lst:addExampleSolution} showcase our global type inference algorithm step by step.
|
\ref{lst:addExampleSolution} showcase the global type inference algorithm step by step.
|
||||||
In this example we know that the type of the variable \texttt{l} is an existential type and has to undergo a capture conversion
|
In the Java code in Listing~\ref{lst:addExample}, the type of variable
|
||||||
|
\texttt{l} is an existential type so that it has to undergo a capture conversion
|
||||||
before being passed to a method call.
|
before being passed to a method call.
|
||||||
This is done by converting the program to A-Normal form \ref{lst:addExampleLet},
|
To this end we convert the program to A-Normal form (Listing~\ref{lst:addExampleLet}),
|
||||||
which introduces a let statement defining a new variable \texttt{v}.
|
which introduces a let statement defining a new variable \texttt{v}.
|
||||||
Afterwards unknown types are replaced by type placeholders ($\tv{v}$ for the type of \texttt{v}) and constraints are generated (see \ref{lst:addExampleCons}).
|
As the type of \texttt{v} is unknown, the algorithm puts a type
|
||||||
These constraints mirror the type rules of our \TamedFJ{} calculus.
|
placeholder $\tv{v}$ for the type of \texttt{v} before generating constraints (see Listing~\ref{lst:addExampleCons}).
|
||||||
|
These constraints mirror the typing rules of the \TamedFJ{} calculus (section~\ref{sec:tifj}).
|
||||||
% During the constraint generation step the type of the variable \texttt{v} is unknown
|
% During the constraint generation step the type of the variable \texttt{v} is unknown
|
||||||
% and given the type placeholder $\tv{v}$.
|
% and given the type placeholder $\tv{v}$.
|
||||||
The methodcall to \texttt{add} spawns the constraint $\tv{v} \lessdotCC \exptype{List}{\wtv{a}}$.
|
The call to \texttt{add} generates the \emph{capture constraint} $\tv{v} \lessdotCC \exptype{List}{\wtv{a}}$.
|
||||||
Here we introduce a capture constraint ($\lessdotCC$) %a new type of subtype constraint
|
This constraint ($\lessdotCC$) is a kind of subtype constraint, which additionally
|
||||||
expressing that the left side of the constraint is subject to a capture conversion.
|
expresses that the left side of the constraint is subject to a capture conversion.
|
||||||
Now our unification algorithm \unify{} (defined in chapter \ref{sec:unify}) is used to solve these constraints.
|
Next, the unification algorithm \unify{} (section~\ref{sec:unify}) solves the constraints.
|
||||||
In the starting set of constraints no type is assigned to $\tv{v}$ yet.
|
Initially, no type is assigned to $\tv{v}$.
|
||||||
During the course of \unify{} more and more types emerge.
|
% During the course of \unify{} more and more types emerge.
|
||||||
As soon as a concrete type for $\tv{v}$ is given \unify{} can conduct a capture conversion if needed.
|
As soon as a concrete type for $\tv{v}$ is appears during constraint
|
||||||
|
solving, \unify{} can conduct a capture conversion if needed.
|
||||||
%The constraints where this is possible are marked as capture constraints.
|
%The constraints where this is possible are marked as capture constraints.
|
||||||
In this example $\tv{v}$ will be set to $\wctype{\wildcard{X}{\type{String}}{\bot}}{List}{\rwildcard{X}}$ leaving us with the following constraints:
|
In this example, $\tv{v}$ will be set to
|
||||||
|
$\wctype{\wildcard{X}{\type{String}}{\bot}}{List}{\rwildcard{X}}$
|
||||||
|
leaving us with the following constraints:
|
||||||
|
|
||||||
\begin{displaymath}
|
\begin{displaymath}
|
||||||
\prftree[r]{Capture}{
|
\prftree[r]{Capture}{
|
||||||
@ -460,17 +469,20 @@ In this example $\tv{v}$ will be set to $\wctype{\wildcard{X}{\type{String}}{\bo
|
|||||||
%which converts a constraint of the form $(\wctype{\rwildcard{X}}{C}{\rwildcard{X}} \lessdotCC \type{T})$ to $(\exptype{C}{\rwildcard{X}} \lessdot \type{T})$
|
%which converts a constraint of the form $(\wctype{\rwildcard{X}}{C}{\rwildcard{X}} \lessdotCC \type{T})$ to $(\exptype{C}{\rwildcard{X}} \lessdot \type{T})$
|
||||||
The constraint $\wctype{\wildcard{X}{\type{Object}}{\type{String}}}{List}{\rwildcard{X}} \lessdotCC \exptype{List}{\wtv{a}}$
|
The constraint $\wctype{\wildcard{X}{\type{Object}}{\type{String}}}{List}{\rwildcard{X}} \lessdotCC \exptype{List}{\wtv{a}}$
|
||||||
allows \unify{} to do a capture conversion to $\exptype{List}{\rwildcard{X}} \lessdot \exptype{List}{\wtv{a}}$.
|
allows \unify{} to do a capture conversion to $\exptype{List}{\rwildcard{X}} \lessdot \exptype{List}{\wtv{a}}$.
|
||||||
The captured wildcard $\rwildcard{X}$ gets a fresh name and is stored in the wildcard environment of the \unify{} algorithm.
|
The captured wildcard $\rwildcard{X}$ gets a fresh name \texttt{Y}, which is stored in the wildcard environment of the \unify{} algorithm.
|
||||||
Leaving us with the solution $\exptype{List}{\rwildcard{Y}} \lessdot \exptype{List}{\rwildcard{Y}}$, $\type{String} \lessdot \rwildcard{Y}$
|
Substituting \texttt{Y} for $\wtv{a}$ yields the constraints almost
|
||||||
The constraint $\type{String} \lessdot \rwildcard{Y}$ is satisfied
|
solved: $\exptype{List}{\rwildcard{Y}} \lessdot
|
||||||
|
\exptype{List}{\rwildcard{Y}}$, $\type{String} \lessdot
|
||||||
|
\rwildcard{Y}$.
|
||||||
|
The first constraint is obviously satisfied and $\type{String} \lessdot \rwildcard{Y}$ is satisfied
|
||||||
because $\rwildcard{Y}$ has $\type{String}$ as lower bound.
|
because $\rwildcard{Y}$ has $\type{String}$ as lower bound.
|
||||||
|
|
||||||
|
|
||||||
A correct Featherweight Java program including all type annotations and an explicit capture conversion via let statement is shown in listing \ref{lst:addExampleSolution}.
|
A correct Featherweight Java program including all type annotations and an explicit capture conversion via let statement is shown in Listing \ref{lst:addExampleSolution}.
|
||||||
This program can be deducted from the type solution of our \unify{} algorithm presented in chapter \ref{sec:unify}.
|
This program can be deduced from the solution of the \unify{} algorithm presented in Section~\ref{sec:unify}.
|
||||||
In the body of the let statement the type $\wctype{\wildcard{X}{\type{Object}}{\type{String}}}{List}{\rwildcard{X}}$
|
In the body of the let statement the type $\wctype{\wildcard{X}{\type{Object}}{\type{String}}}{List}{\rwildcard{X}}$
|
||||||
becomes $\exptype{List}{\rwildcard{X}}$ and the wildcard $\wildcard{X}{\type{Object}}{\type{String}}$ is free and can be used as
|
becomes $\exptype{List}{\rwildcard{X}}$ and the capture converted wildcard $\wildcard{X}{\type{Object}}{\type{String}}$ can be used as
|
||||||
a type parameter to method call \texttt{<X>add(v, "String")}.
|
a type parameter of the call \texttt{<X>add(v, "String")}.
|
||||||
|
|
||||||
% The input to our type inference algorithm is a modified version of the calculus in \cite{WildcardsNeedWitnessProtection} (see chapter \ref{sec:tifj}).
|
% The input to our type inference algorithm is a modified version of the calculus in \cite{WildcardsNeedWitnessProtection} (see chapter \ref{sec:tifj}).
|
||||||
% First \fjtype{} (see section \ref{chapter:constraintGeneration}) generates constraints
|
% First \fjtype{} (see section \ref{chapter:constraintGeneration}) generates constraints
|
||||||
@ -496,32 +508,39 @@ a type parameter to method call \texttt{<X>add(v, "String")}.
|
|||||||
|
|
||||||
% do not substitute free type variables
|
% do not substitute free type variables
|
||||||
|
|
||||||
Global Type inference for Featherweight Java with generics but without wildcards is solved already.
|
Global type inference for Featherweight Generic Java without wildcards
|
||||||
But adding Wildcards to the calculus creates new problems we did not foresee
|
has been considered elsewher \cite{TIforFGJ}.
|
||||||
and which have not been recognized by an existing type unification algorithm for Java with wildcards \ref{plue09_1}.
|
Adding wildcards to the calculus created new problems, which have not
|
||||||
|
been recognized by existing work on type unification for Java with wildcards~\cite{plue09_1}.
|
||||||
% what is the problem?
|
% what is the problem?
|
||||||
% Java does invisible capture conversion steps
|
% Java does invisible capture conversion steps
|
||||||
Java Wildcard types are represented as existential types and have to be opened before they can be used.
|
Java's wildcard types are represented as existential types that have to be opened before they can be used.
|
||||||
This can either be done implicitly (\cite{aModelForJavaWithWildcards}, \cite{javaTIisBroken}) or explicitly via let statements (\cite{WildcardsNeedWitnessProtection}).
|
Opening can either be done implicitly
|
||||||
For all of those variations it is vital to know the argument types with which a method is called.
|
(\cite{aModelForJavaWithWildcards}, \cite{javaTIisBroken}) or
|
||||||
But our input program does not contain any type annotations.
|
explicitly via a let statement (\cite{WildcardsNeedWitnessProtection}).
|
||||||
We do not know where an existential type will emerge and where a capture conversion is necessary.
|
For all variations it is vital to know the argument types with which a method is called.
|
||||||
This has to be figured out during the type inference algorithm.
|
In the absence of type annotations,
|
||||||
We detected three main challenges related to Java Wildcards and Global Type Inference:
|
we do not know where an existential type will emerge and where a capture conversion is necessary.
|
||||||
|
Rather, the type inference algorithm has to figure out the placement
|
||||||
|
of existentials.
|
||||||
|
We identified three main challenges related to Java wildcards and global type inference.
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
\item \label{challenge:1}
|
\item \label{challenge:1}
|
||||||
One challenge is to design the algorithm in a way that it finds a correct solution for the program shown in listing \ref{lst:addExample}
|
One challenge is to design the algorithm in a way that it finds a
|
||||||
and rejects the program in listing \ref{lst:concatError}.
|
correct solution for programs like the one shown in Listing~\ref{lst:addExample}
|
||||||
|
and rejects programs like the one in Listing~\ref{lst:concatError}.
|
||||||
The first one is a valid Java program,
|
The first one is a valid Java program,
|
||||||
because the type \texttt{List<? super String>} is \textit{capture converted} to a fresh type variable $\rwildcard{X}$
|
because the type \texttt{List<? super String>} is \textit{capture converted} to a fresh type variable $\rwildcard{X}$
|
||||||
which is used as the generic method parameter for the call to \texttt{add} as shown in listing \ref{lst:addExampleLet}.
|
which is used as the generic method parameter for the call to \texttt{add} as shown in Listing~\ref{lst:addExampleLet}.
|
||||||
Knowing that the type \texttt{String} is a subtype of the free variable $\rwildcard{X}$
|
Because we know that the type \texttt{String} is a subtype of the free variable $\rwildcard{X}$,
|
||||||
it is safe to pass \texttt{"String"} for the first parameter of the function.
|
it is safe to pass \texttt{"String"} for the first parameter of the function.
|
||||||
|
|
||||||
The second program shown in listing \ref{lst:concatError} is incorrect.
|
The second program shown in Listing~\ref{lst:concatError} is incorrect.
|
||||||
The method call to \texttt{concat} with two wildcard lists is unsound.
|
The method call to \texttt{concat} with two wildcard lists is unsound.
|
||||||
Each list could be of a different kind and therefore the \texttt{concat} cannot succeed.
|
The element types of the lists may be unrelated, therefore the call to \texttt{concat} cannot succeed.
|
||||||
The problem gets apparent when we try to write the \texttt{concat} method call in our \TamedFJ{} calculus (listing \ref{lst:concatTamedFJ}):
|
The problem gets apparent if we try to write the \texttt{concat}
|
||||||
|
method call in the \TamedFJ{} calculus
|
||||||
|
(Listing~\ref{lst:concatTamedFJ}):
|
||||||
\texttt{l1'} and \texttt{l2'} are two different lists inside the body of the let statements, namely
|
\texttt{l1'} and \texttt{l2'} are two different lists inside the body of the let statements, namely
|
||||||
$\exptype{List}{\rwildcard{X}}$ and $\exptype{List}{\rwildcard{Y}}$.
|
$\exptype{List}{\rwildcard{X}}$ and $\exptype{List}{\rwildcard{Y}}$.
|
||||||
For the method call \texttt{concat(x1, x2)} no replacement for the generic \texttt{A}
|
For the method call \texttt{concat(x1, x2)} no replacement for the generic \texttt{A}
|
||||||
@ -533,33 +552,33 @@ $\exptype{List}{\type{A}} <: \exptype{List}{\type{X}},
|
|||||||
% Capture Conversion during Unify.
|
% Capture Conversion during Unify.
|
||||||
|
|
||||||
\item
|
\item
|
||||||
\unify{} morphs a constraint set into a correct type solution
|
\unify{} transforms a constraint set into a correct type solution by
|
||||||
gradually assigning types to type placeholders during that process.
|
gradually assigning types to type placeholders during that process.
|
||||||
Solved constraints are removed and never considered again.
|
Solved constraints are removed and never considered again.
|
||||||
In the following example \unify{} solves the constraint generated by the expression
|
In the following example, \unify{} solves the constraint generated by the expression
|
||||||
\texttt{l.add(l.head())} first, which results in $\ntv{l} \lessdot \exptype{List}{\wtv{a}}$.
|
\texttt{l.add(l.head())} first, which results in $\ntv{l} \lessdot \exptype{List}{\wtv{a}}$.
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
anyList() = new List<String>() :? new List<Integer>()
|
anyList() = new List<String>() :? new List<Integer>()
|
||||||
|
|
||||||
add(anyList(), anyList().head());
|
add(anyList(), anyList().head());
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
The type for \texttt{l} can be any kind of list, but it has to be a invariant one.
|
The type for \texttt{l} can be an arbitrary list, but it has to be a invariant one.
|
||||||
Assigning a \texttt{List<?>} for \texttt{l} is unsound, because the type list hiding behind
|
Assigning a \texttt{List<?>} for \texttt{l} is unsound, because the type list hiding behind
|
||||||
\texttt{List<?>} could be a different one for the \texttt{add} call than the \texttt{head} method call.
|
\texttt{List<?>} could be a different one for the \texttt{add} call than the \texttt{head} method call.
|
||||||
An additional constraint $\wctype{\rwildcard{X}}{List}{\rwildcard{X}} \lessdot \exptype{List}{\wtv{a}}$
|
An additional constraint $\wctype{\rwildcard{X}}{List}{\rwildcard{X}} \lessdot \exptype{List}{\wtv{a}}$
|
||||||
is solved by removing the wildcard $\rwildcard{X}$ if possible.
|
is solved by removing the wildcard $\rwildcard{X}$ if possible.
|
||||||
|
|
||||||
this problem is solved by ANF transformation
|
This problem is solved by ANF transformation
|
||||||
|
|
||||||
\item
|
\item
|
||||||
% This problem is solved by assuming everything is a wildcard and lateron erasing excessive wildcards
|
% This problem is solved by assuming everything is a wildcard and lateron erasing excessive wildcards
|
||||||
% this is solved by having wildcards bound to a type. But this makes it necessary to remove wildcards lateron otherwise Unify would have to backtrack
|
% this is solved by having wildcards bound to a type. But this makes it necessary to remove wildcards lateron otherwise Unify would have to backtrack
|
||||||
The program in listing \ref{shuffleExample} shows a challenge involving wildcards and subtyping.
|
The program in Listing~\ref{shuffleExample} exhibits a challenge involving wildcards and subtyping.
|
||||||
The method call \texttt{shuffle(l)} is incorrect, because \texttt{l} has the type
|
The method call \texttt{shuffle(l)} is incorrect, because \texttt{l} has type
|
||||||
$\exptype{List}{\wctype{\rwildcard{X}}{List}{\rwildcard{X}}}$ representing a list of unknown lists.
|
$\exptype{List}{\wctype{\rwildcard{X}}{List}{\rwildcard{X}}}$ representing a list of unknown lists.
|
||||||
Whereas $\wctype{\rwildcard{X}}{List2D}{\rwildcard{X}}$ is a subtype of
|
However, $\wctype{\rwildcard{X}}{List2D}{\rwildcard{X}}$ is a subtype of
|
||||||
$\wctype{\rwildcard{X}}{List}{\exptype{List}{\rwildcard{X}}}$ representing a list of lists, all of the same type $\rwildcard{X}$,
|
$\wctype{\rwildcard{X}}{List}{\exptype{List}{\rwildcard{X}}}$ which represents a list of lists, all of the same type $\rwildcard{X}$,
|
||||||
and can savely be passed to \texttt{shuffle}.
|
and can be passed safely to \texttt{shuffle}.
|
||||||
This behavior can also be explained by looking at the types and their capture converted versions:
|
This behavior can also be explained by looking at the types and their capture converted versions:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tabular}{l | l | l}
|
\begin{tabular}{l | l | l}
|
||||||
@ -619,7 +638,7 @@ shuffle(l2d); // Valid
|
|||||||
% \end{center}
|
% \end{center}
|
||||||
% \end{constraintset}
|
% \end{constraintset}
|
||||||
|
|
||||||
Given such a program our type inference algorithm has to allow the call \texttt{shuffle(l2d)} and
|
Given such a program the type inference algorithm has to allow the call \texttt{shuffle(l2d)} and
|
||||||
decline the call to \texttt{shuffle(l)}.
|
decline the call to \texttt{shuffle(l)}.
|
||||||
|
|
||||||
% The method call \texttt{shuffle(l)} is invalid however,
|
% The method call \texttt{shuffle(l)} is invalid however,
|
||||||
@ -628,10 +647,10 @@ decline the call to \texttt{shuffle(l)}.
|
|||||||
% There is no solution for the subtype constraint:
|
% There is no solution for the subtype constraint:
|
||||||
% $\exptype{List}{\wctype{\rwildcard{X}}{List}{\rwildcard{X}}} \lessdotCC \exptype{List}{\exptype{List}{\wtv{x}}}$
|
% $\exptype{List}{\wctype{\rwildcard{X}}{List}{\rwildcard{X}}} \lessdotCC \exptype{List}{\exptype{List}{\wtv{x}}}$
|
||||||
|
|
||||||
\item \label{challenge3} \textbf{Free Variables cannot leaver their scope}:
|
\item \label{challenge3} \textbf{Free variables cannot leave their scope}:
|
||||||
Let's assume we have a variable \texttt{ls} with type $\exptype{List}{\wctype{\rwildcard{X}}{List}{\rwildcard{X}}}$
|
Let's assume we have a variable \texttt{ls} with type $\exptype{List}{\wctype{\rwildcard{X}}{List}{\rwildcard{X}}}$
|
||||||
%When calling the \texttt{id} function with an element of this list we have to apply capture conversion.
|
%When calling the \texttt{id} function with an element of this list we have to apply capture conversion.
|
||||||
and the following input program:
|
and the following program:
|
||||||
|
|
||||||
\noindent
|
\noindent
|
||||||
\begin{minipage}{0.62\textwidth}
|
\begin{minipage}{0.62\textwidth}
|
||||||
@ -648,14 +667,15 @@ let x : (*@$\wctype{\rwildcard{X}}{List}{\rwildcard{X}}$@*) = ls.get(0) in id(x)
|
|||||||
% the variable z has to
|
% the variable z has to
|
||||||
|
|
||||||
Take the Java program in listing \ref{lst:mapExample} for example.
|
Take the Java program in listing \ref{lst:mapExample} for example.
|
||||||
It maps every element of a list
|
It uses map to apply a polymorphic method \texttt{id} to every element of a list
|
||||||
$\expr{l} : \exptype{List}{\wctype{\rwildcard{A}}{List}{\rwildcard{A}}}$
|
$\expr{l} :
|
||||||
to a polymorphic \texttt{id} method.
|
\exptype{List}{\wctype{\rwildcard{A}}{List}{\rwildcard{A}}}$.
|
||||||
We want to use our \unify{} algorithm to determine the correct type for the
|
|
||||||
variable \expr{l2}.
|
How do we get the \unify{} algorithm to determine the correct type for the
|
||||||
|
variable \expr{l2}?
|
||||||
Although we do not specify constraint generation for language constructs like
|
Although we do not specify constraint generation for language constructs like
|
||||||
lambda expressions used in this example,
|
lambda expressions used in this example,
|
||||||
we can imagine that the constraints have to look something like this:
|
we can imagine that the constraints have to look like in Listing~\ref{lst:mapExampleCons}.
|
||||||
|
|
||||||
\begin{minipage}{0.45\textwidth}
|
\begin{minipage}{0.45\textwidth}
|
||||||
\begin{lstlisting}[caption=List Map Example,label=lst:mapExample]
|
\begin{lstlisting}[caption=List Map Example,label=lst:mapExample]
|
||||||
@ -682,16 +702,18 @@ stem from the body of the lambda expression
|
|||||||
|
|
||||||
The T-Let rule prevents us from using free variables created by the method call to \expr{id}
|
The T-Let rule prevents us from using free variables created by the method call to \expr{id}
|
||||||
to be used in the return type $\tv{z}$.
|
to be used in the return type $\tv{z}$.
|
||||||
But this has to be signaled to the \unify{} algorithm, which does not know about the origin and context of
|
But this restriction has to be communicated to the \unify{} algorithm,
|
||||||
|
which does not know about the origin and context of
|
||||||
the constraints.
|
the constraints.
|
||||||
If we naively substitute $\sigma(\tv{z}) = \exptype{List}{\rwildcard{A}}$
|
If we naively substitute $\sigma(\tv{z}) = \exptype{List}{\rwildcard{A}}$
|
||||||
the return type of the \texttt{map} function would be the type $\exptype{List}{\exptype{List}{\rwildcard{A}}}$.
|
the return type of the \texttt{map} function would be the type
|
||||||
This type solution is unsound.
|
$\exptype{List}{\exptype{List}{\rwildcard{A}}}$, which would be unsound.
|
||||||
The type of \expr{l2} is the same as the one of \expr{l}:
|
The type of \expr{l2} is the same as the one of \expr{l}:
|
||||||
$\exptype{List}{\wctype{\rwildcard{A}}{List}{\rwildcard{A}}}$
|
$\exptype{List}{\wctype{\rwildcard{A}}{List}{\rwildcard{A}}}$
|
||||||
|
|
||||||
\textbf{Solution:}
|
\textbf{Solution:}
|
||||||
We solve this by distinguishing between wildcard placeholders and normal placeholders.
|
We solve this issue by distinguishing between wildcard placeholders
|
||||||
|
and normal placeholders and introducing them as needed.
|
||||||
$\ntv{z}$ is a normal placeholder and is not allowed to contain free variables.
|
$\ntv{z}$ is a normal placeholder and is not allowed to contain free variables.
|
||||||
|
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
Loading…
Reference in New Issue
Block a user