
\documentclass[12pt]{article}
\usepackage[T2A,OT1]{fontenc}
\usepackage[default]{cantarell}
\usepackage[a4paper, top=20mm, bottom=20mm, left=20mm, right=20mm]{geometry}
\usepackage[utf8]{inputenc}
\usepackage[russian, english]{babel}
\usepackage{tabu}
\usepackage{hyperref}
\usepackage{parskip}
\usepackage{graphicx}
\usepackage{tabularx}
\usepackage[normalem]{ulem}
\usepackage{float}
\floatstyle{boxed}
\restylefloat{figure}
\usepackage{setspace}
\onehalfspacing
\author{Artyom Bologov \href{mailto:lost-compute@aartaka.me}{(email)}}
\date{\today}
\title{Lost Computation}
\makeatletter
\def\endenv{\expandafter\end\expandafter{\@currenvir}}
\makeatother
\begin{document}
\maketitle

\includegraphics[width=\textwidth,height=\textheight,keepaspectratio]{./assets/lost-compute.png}

It immensely saddens me when the computation and context is lost.
Because it’s there for a reason.
More context means better error messages and debugging state preservation.
And these result in better developer and user experience.
Yet we programmers constantly lose running computation and its context.
What a waste.

\section*{Case Study: a Web Server} \label{server}

So imagine a simple Web server.
A set of request handlers, thread pool, some dispatcher running handlers on requests.
Every handler does work on the request, like querying/modifying DB, crunching numbers, or whatever.
Handlers also rely on session data (cookies, API tokens, browser fingerprints) to make decisions.
Clear so far, right?

Now what if something goes wrong at the DB level?
An exception / error / condition is thrown.
Depending on who’s catching it, the consequences differ:

\begin{description}\item[Request handler]
Has all the context and can return reasonable error message/status.
  But every request handler needs its own \verb|catch| clause, and usually more than one.
\item[Handler dispatcher]
Is more generic than request handler, so lacks some context, like handler stack and interim values.
  Can only return generic error message/status.
\item[Thread-specific catches]
Even more generic, lack the whole context of Web Server except for some global stats.
  404 and 500.
\item[A whole application]
No idea what’s going on, I guess I just collapse.
\end{description}

You get the trade-offs: catching errors at the leaves of the app is tedious and repetitive.
While catching up the branches is too generic and loses context and running computation.

\section*{Solution: Break Loops and Dynamic Environment Handlers} \label{environment}

It’s a shame most programming languages adopted the exception-driven stack-unwinding model of errors.
Because these are to blame for lost state.
Luckily, some programming systems have more reliable error handling that doesn’t lose state/compute.

Common Lisp, for example.
There’s this \verb|handler-bind| block that catches an error and acts in its dynamic environment.
Meaning: all the state of e.g. erroring request is there for the handler to act on.
With the ability to \verb|restart| (effectively continue)
the whole computation from exactly where it stopped once you fix the state.
Sometimes even interactively, if the user wants to.

\href{https://reddit.com/r/lisp/comments/1l8zth6/lost_computation_a_lisper_crying_over_stack}{If I'm not explaining it well enough, read the Reddit discussion instead}.
Here’s an excerpt:

\begin{figure}[h!]\begin{quote}
Yes, CL basically catches the error in the driver. When an error is signalled, rather than unwinding, the handler is invoked. The handler can then choose which, of potentially several, restarts to jump up the stack to. Rather than unwinding to the handler unconditionally, the handler chooses where to transfer control to.
\end{quote}\caption{u/phalp in the discussion on Lost Compute on Reddit}\end{figure}

See also Vincent Dardel’s
\href{https://lisp-journey.gitlab.io/blog/lisp-error-handling-how-handler-bind-does-not-unwind-the-stack}{post on handler-bind magic of stack preservation}.

I know this type of error environment inspection is a luxury that most languages don’t provide.
But it’s also a proof for what’s possible, so why settle for less?

\section*{Another Solution: Context Passing} \label{context}

But what if we didn’t throw at all?
If we return error values, then we don’t lose context we can store in them.
That’s what Go (second return value,) Haskell (Left/Right,) and Clojure (context maps) settled on.

\href{https://daveliepmann.com/articles/idiomatic-clojure-errors}{Dave Liepmann wrote on Clojure’s approaches to errors}.
So I’ll just refer you to his post for a better overview.

The problem is that one has to explicitly pass the state along with an error.
And no one wants to do that.
So this approach is worse than the automatic environment inspection that Lisp allows.
(And it still means terminating (albeit normally) at least a part of the running computation on error.)
But still better than destroying all the state.

\par\noindent\rule{\textwidth}{0.4pt}

Whatever way to preserve the erroring state you pick, I beg of you.
Please, don’t lose computation.


\par\noindent\rule{\textwidth}{0.4pt}
\href{https://creativecommons.org/licenses/by/4.0}{CC-BY 4.0} 2022-2026 by Artyom Bologov (aartaka,)
\href{https://codeberg.org/aartaka/pages/commit/a91befa}{with one commit remixing Claude-generated code}.
Any and all opinions listed here are my own and not representative of my employers; future, past and present.
\end{document}
