Skip to content

Commit

Permalink
Apply suggestions from code review
Browse files Browse the repository at this point in the history
Co-authored-by: wiktorkuchta <35867657+wiktorkuchta@users.noreply.github.com>
Co-authored-by: David Allsopp <david.allsopp@metastack.com>
Co-authored-by: Konstantin Romanov <kromanov1@bloomberg.net>
  • Loading branch information
4 people committed Nov 26, 2021
1 parent 54384ed commit 73c2481
Showing 1 changed file with 24 additions and 24 deletions.
48 changes: 24 additions & 24 deletions manual/src/refman/extensions/tail_mod_cons.etex
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ operating systems with limited stack space -- the dreaded
"Stack_overflow" exception.

\begin{caml_example}{toplevel}
List.length (map Fun.id (List.init 1_000_000 (fun i -> i)));;
List.length (map Fun.id (List.init 1_000_000 Fun.id));;
\end{caml_example}

In this implementation of "map", the recursive call happens in
Expand Down Expand Up @@ -52,7 +52,7 @@ let[@tail_mod_cons] rec map f l =
\end{caml_example*}

\begin{caml_example}{toplevel}
List.length (map Fun.id (List.init 1_000_000 (fun i -> i)));;
List.length (map Fun.id (List.init 1_000_000 Fun.id));;
\end{caml_example}

This transformation only improves calls in tail-modulo-cons position,
Expand Down Expand Up @@ -178,17 +178,17 @@ let[@tail_mod_cons] rec map_vars f exp =
\end{caml_example*}

To disambiguate, the user should add a "[\@tailcall]" attribute to the
recursive call that they want to see in tail position:
recursive call that should be transformed to tail position:
\begin{caml_example*}{verbatim}
let[@tail_mod_cons] rec map_vars f exp =
match exp with
| Var v -> Var (f v)
| Let ((v, def), body) ->
Let ((f v, map_vars f def), (map_vars[@tailcall]) f body)
\end{caml_example*}
Beware that the resulting function is \emph{not} tail-recursive, the
Be aware that the resulting function is \emph{not} tail-recursive, the
recursive call on "def" will consume stack space. However, expression
trees tend to unbalanced on the right (lots of "Let" in sequence,
trees tend to be right-leaning (lots of "Let" in sequence,
rather than nested inside each other), so putting the call on "body"
in tail position is an interesting improvement over the naive
definition: it gives bounded stack space consumption if we assume
Expand All @@ -215,7 +215,7 @@ Due to the nature of the tail-mod-cons transformation
positions.
\item Calls from a function \emph{not} marked tail-mod-cons to
a tail-mod-cons function or, conversely, from a tail-mod-cons
function to a not-tail-mod-cons function are \emph{not} tail calls,
function to a non-tail-mod-cons function are \emph{not} tail calls,
even if they syntactically appear in tail position in the source
program.
\end{itemize}
Expand All @@ -237,7 +237,7 @@ let[@tail_mod_cons] rec flatten = function
\end{caml_example*}
Here the "append_flatten" helper is not annotated with
"[\@tail_mod_cons]", so the calls "append_flatten xs xss" and "flatten
xss" will \emph{not} be tail calls. The right fix here is to mark
xss" will \emph{not} be tail calls. The correct fix here is to mark
"append_flatten" to be tail-mod-cons.

\begin{caml_example*}{verbatim}
Expand Down Expand Up @@ -303,8 +303,8 @@ let[@tail_mod_cons] rec map_vars f exp =
Let ((f v, self def), (self[@tailcall]) body)
\end{caml_example*}

In other cases, there is no benefit in making the called function
tail-mod-cons, or it is not possible, for example it is a function
In other cases, there is either no benefit in making the called function
tail-mod-cons, or it is not possible: for example, it is a function
parameter (the transformation only works with direct calls to
known functions).

Expand Down Expand Up @@ -394,7 +394,7 @@ something that we could improve in the future); but this call happens
only once when invoking "map f l", with all list elements after the
first one processed in constant stack by "map_dps".

This explains the ``getting ouf of tail-mod-cons''
This explains the ``getting out of tail-mod-cons''
subtleties. Consider our previous example involving mutual recursion
between "flatten" and "append_flatten".
\begin{verbatim}
Expand Down Expand Up @@ -438,12 +438,12 @@ destination-passing-style version and become tail calls:
\begin{itemize}
\item The transformation is local: only tail-mod-cons calls to "foo"
within the same compilation unit as "foo" become tail calls.
\item The transformation is first-order: only direct calls that
a known tail-mod-cons functions become tail calls when in
\item The transformation is first-order: only direct calls to
known tail-mod-cons functions become tail calls when in
tail-mod-cons position, never calls to function parameters.
\end{itemize}

Consier the call "Option.map foo x" for example: even if "foo" is
Consider the call "Option.map foo x" for example: even if "foo" is
called in tail-mod-cons position within the definition of
"Option.map", that call will never become a tail call. (This would the
case even if the call to "Option.map" was inside the "Option"
Expand All @@ -452,7 +452,7 @@ module.)
In general this limitation is not a problem for recursive functions:
the first call from an outside module or a higher-order function will
consume stack space, but further recursive calls in tail-mod-cons
position will get optimized. For example if "List.map" is defined as
position will get optimized. For example, if "List.map" is defined as
a tail-mod-cons function, calls from outside the "List" module will
not become tail calls when in tail positions, but the recursive calls
within the definition of "List.map" are in tail-modulo-cons positions
Expand All @@ -463,24 +463,24 @@ constant space.
These limitations may be an issue in more complex situations where
mutual recursion happens between functions, with some functions not
marked tail-mod-cons, or defined across different modules, or called
indirectly, for example thourgh function paramaters.
indirectly, for example through function parameters.

\paragraph{Non-exact calls to tupled functions} OCaml performs an
implicit optimization for "tupled" functions, which take a single
implicit optimization for ``tupled'' functions, which take a single
parameter that is a tuple: "let f (x, y, z) = ...". Direct calls to
these functions whose argument is a tuple literal "f (a, b, c)" will
call the "tupled" function passing three parameters directly, instead
of building a tuples. Other calls, either indirect calls or calls
passing a more complex tuple value ("let t = (a, b, c) in f t") are
these functions with a tuple literal argument (like "f (a, b, c)") will
call the ``tupled'' function by passing the parameters directly, instead
of building a tuple of them. Other calls, either indirect calls or calls
passing a more complex tuple value (like "let t = (a, b, c) in f t") are
compiled as ``inexact'' calls that go through a wrapper.

The "[\@tail_mod_cons]" transformation supports tupled functions, but
will only optimize ``exact'' calls in tail position; direct calls to
something else than a tuple literal will not become tail calls. The
something other than a tuple literal will not become tail calls. The
user can manually unpack a tuple to force a call to be ``exact'': "let
(x, y, z) = t in f (x, y, z)". If there is any doubt of whether a call
can be tail-mod-cons-optimized or not, you can use the "[\@tailcall]"
annotation on the called function, which will warn if the
(x, y, z) = t in f (x, y, z)". If there is any doubt as to whether a call
can be tail-mod-cons-optimized or not, one can use the "[\@tailcall]"
attribute on the called function, which will warn if the
transformation is not possible.

\begin{caml_example*}{verbatim}[warning=51]
Expand Down

0 comments on commit 73c2481

Please sign in to comment.