Browse Source

Tidy up text on tail recursion.

master
Daniel Hillerström 5 years ago
parent
commit
63c917e533
  1. 120
      thesis.tex

120
thesis.tex

@ -21,7 +21,8 @@
\usepackage{caption,subcaption} % Sub figures support
\DeclareCaptionFormat{underlinedfigure}{#1#2#3\hrulefill}
\captionsetup[figure]{format=underlinedfigure}
\usepackage[T1]{fontenc} % Fixes font issues
\usepackage[T1]{fontenc} % Fixes issues with accented characters
%\usepackage{libertine}
%\usepackage{lmodern}
\usepackage[activate=true,
final,
@ -31,13 +32,6 @@
factor=1100,
stretch=10,
shrink=10]{microtype}
\usepackage{enumerate} % Customise enumerate-environments
\usepackage{xcolor} % Colours
\usepackage{xspace} % Smart spacing in commands.
\usepackage{tikz}
\usetikzlibrary{fit,calc,trees,positioning,arrows,chains,shapes.geometric,%
decorations.pathreplacing,decorations.pathmorphing,shapes,%
matrix,shapes.symbols,intersections}
\SetProtrusion{encoding={*},family={bch},series={*},size={6,7}}
{1={ ,750},2={ ,500},3={ ,500},4={ ,500},5={ ,500},
@ -51,6 +45,14 @@
\textquotedblleft={ ,150}, % left quotation mark, space from right
\textquotedblright={150, }} % right quotation mark, space from left
\usepackage{enumerate} % Customise enumerate-environments
\usepackage{xcolor} % Colours
\usepackage{xspace} % Smart spacing in commands.
\usepackage{tikz}
\usetikzlibrary{fit,calc,trees,positioning,arrows,chains,shapes.geometric,%
decorations.pathreplacing,decorations.pathmorphing,shapes,%
matrix,shapes.symbols,intersections}
\usepackage[customcolors,shade]{hf-tikz} % Shaded backgrounds.
\hfsetfillcolor{gray!40}
\hfsetbordercolor{gray!40}
@ -881,61 +883,87 @@ bindings, pair deconstructing, and case splitting. In each of those
cases we subtract the relevant binder(s) from the set of free
variables.
\paragraph{Proper tail-recursion}
\paragraph{Tail recursion}
Many implementations of functional programming languages to be
tail-recursive in order to enable unbounded iteration as otherwise
nested repeated function calls would quickly run out of space on a
computer.
In practice implementations of functional programming languages tend
to be tail-recursive in order to enable unbounded iteration. Otherwise
nested (repeated) function calls would quickly run out of stack space
on a conventional computer.
%
Intuitively, tail-recursion permits an already allocated stack frame
for some on-going function call to be reused by a nested function
call, provided that this nested call is the last thing to occur before
returning from the on-going function call.
%
A special case is when the nested function call is a fresh invocation
of the on-going function call, i.e. a self-reference. In this case the
nested function call is known as a \emph{tail recursive call},
otherwise it is simply known as a \emph{tail call}.
%
Thus the qualifier ``tail-recursive'' may be somewhat confusing as for
an implementation to be tail-recursive it must support recycling of
stack frames for tail calls; it is not sufficient to support tail
recursive calls.
%
Any decent implementation of Standard ML~\cite{MilnerTHM97},
OCaml~\cite{LeroyDFGRV20}, or Scheme~\cite{SperberDFSFM10} will be
tail-recursive. I intentionally say implementation, because it is
often the case that the specification or user manual does not
explicitly require a suitable implementation to be tail-recursive; in
fact of the three languages just mentioned only Scheme mandates an
implementation to be tail-recursive.
%
The Scheme specification actually goes further and demands that an
implementation is \emph{properly tail-recursive}, which provides
strict guarantees on the asymptotic space consumption of tail
calls~\cite{Clinger98}.
Tail calls will become important in Chapter~\ref{ch:cps} where we will
get to discuss continuation passing style as an implementation
technique for effect handlers, as tail calls are ubiquitous in
continuation passing style.
%
Therefore let us formally characterise tail calls by adapting a
syntactic characterisation due to \citet{Clinger98}.
%
\begin{definition}[Tail computation]\label{def:tail-comp}
The notion of tail computation is defined inductively as follows.
tail-recursive. I deliberately say implementation rather than
specification, because it is often the case that the specification or
the user manual do not explicitly require a suitable implementation to
be tail-recursive; in fact of the three languages just mentioned only
Scheme explicitly mandates an implementation to be
tail-recursive~\cite{SperberDFSFM10}.
%
% The Scheme specification actually goes further and demands that an
% implementation is \emph{properly tail-recursive}, which provides
% strict guarantees on the asymptotic space consumption of tail
% calls~\cite{Clinger98}.
Tail calls will become important in Chapter~\ref{ch:cps} when we will
discuss continuation passing style as an implementation technique for
effect handlers, as tail calls happen to be ubiquitous in continuation
passing style.
%
Therefore let us formally characterise tail calls.
%
It is safest to use a syntactic characterisation, as opposed to a
semantic characterisation, because in the presence of control effects
(which we will add in Chapter~\ref{ch:unary-handlers}) it surprisingly
tricky to describe tail calls in terms of control flow such as ``the
last thing to occur before returning from the enclosing function'' as
a function may return multiple times. In particular, the effects of a
function may be replayed several times.
%
For this reason we will adapt a syntactic characterisation of tail
calls due to \citet{Clinger98}. First, we define what it means for a
computation to syntactically \emph{appear in tail position}.
%
\begin{definition}[Tail position]\label{def:tail-comp}
Tail position is a transitive notion for computation terms, which is
defined inductively as follows.
%
\begin{itemize}
\item The body $M$ of a $\lambda$-abstraction ($\lambda x. M$) is a
tail computation.
\item If $\Case\;V\;\{\ell\,x \mapsto M; y \mapsto N\}$ is a tail
computation, then both $M$ and $N$ are tail computations.
\item If $\Let\;\Record{\ell = x; y} = V \;\In\;N$ is a tail
computation, then $N$ is a tail computation.
\item If $\Let\;x \revto M\;\In\;N$ is a tail computation, then
$N$ is a tail computation.
\item Nothing else is a tail computation.
\item The body $M$ of a $\lambda$-abstraction ($\lambda x. M$) appears in
tail position.
\item If $\Case\;V\;\{\ell\,x \mapsto M; y \mapsto N\}$ appears in tail
position, then both $M$ and $N$ appear in tail positions.
\item If $\Let\;\Record{\ell = x; y} = V \;\In\;N$ appears in tail
position, then $N$ is in tail position.
\item If $\Let\;x \revto M\;\In\;N$ appear in tail position, then
$N$ appear in tail position.
\item Nothing else appears in tail position.
\end{itemize}
\end{definition}
%
\begin{definition}[Tail call]\label{def:tail-call}
A tail call is tail computation that has the form $V\,W$.
An application term $V\,W$ is said to be a tail call if it appears
in tail position.
\end{definition}
%
The syntactic position of a tail call is often referred to as the
\emph{tail-position}.
% The syntactic position of a tail call is often referred to as the
% \emph{tail-position}.
%
\subsection{Typing rules}

Loading…
Cancel
Save