John Fremlin's blog: Lambdas as classes

Posted 2018-12-16 23:00:00 GMT

A class is just a closure with an arbitrary set of entry points.

A closure is a function defined inline. Commonly it's called a lambda. It may capture some values from the enclosing scope. These are stored in memory. They're laid out just as if a class or structure was defined that contained them.

In fact, many compilers will lay them out exactly the same way.

#include <iostream>
class Point 
  int x;
  int y;
  Point(int x_, int y_):x(x_),y(y_){}
  int d(int ox, int oy) 
    return (ox-x)*(ox-x)+(oy-y)*(oy-y);
void dump_mem(char const* name, int const*p) {
  std::cout << name << "\t@" << p << "\t " << p[0] << ',' << p[1] << std::endl;
int main() {
  int x=1337; int y=65537;
  Point p{x,y};
  auto d = [=](int ox,int oy) {
    return (ox-x)*(ox-x)+(oy-y)*(oy-y);
  dump_mem("class", (int*)&p);
  dump_mem("lambda", (int*)&d);

On my computer this outputs

class   @0x7ffcd00fd748  1337,65537
lambda  @0x7ffcd00fd750  1337,65537

Lambdas are like classes and vice versa.

Some languages, like C++, let you create a class that can behave like a lambda by having a default entry point. Other languages do not.

In some languages, it's inconvenient to create classes but easy to pass around lambdas. Then you can pass around collections of lambdas that touch the same bound variables, just like a class.

Beyond this trivial equivalence, it's actually useful for structuring problems. Particularly, in coding interviews where concision is very valuable and the problem is neat. It lets you close over multiple variables and not pass them to every function.

Here is an example of a solution to the small problem for this excellent Google Codejam Kickstart question. We obtain the concision of global variables, without the downside of singleton global state.

#include <iostream>
#include <vector>
#include <cstdlib>
#include <limits>
#include <cassert>
using namespace std;
struct MaxSweetness {
// problem defined inputs
  long N, O, D;
  long X1, X2, A, B, C, M, L;
  vector<long> S;
// solution state variables
  long best_so_far;
  bool found_any;
  long sweetness;
  long odds;
  auto& read(std::istream&is) {
    is >> N >> O  >> D
       >> X1 >> X2 >> A >> B >> C >> M >> L;
    return *this;
  auto& build_sweetness() {
    unsigned j=0;
    long X_1 = X2;
    long X_2 = X1;
    S[j++] = X1 + L;
    S[j++] = X2 + L;
    for (; j < S.size(); ++j) {
      long X_0 = (A * X_1 + B * X_2 + C)%M;
      X_2 = X_1;
      X_1 = X_0;
      S[j] = X_0 + L;
    return *this;
  void add_candy(long index) {
    auto val = S[index];
    odds += abs(val) % 2;
    sweetness += val;
  // can Supervin eat more candy?
  bool unlimited() {
    return odds <= O && sweetness <= D;
  void check_sweetness() 
    if (unlimited()) {
      best_so_far = max(best_so_far, sweetness);
      found_any = true;
  void del_candy(long index) {
    auto val = S[index];
    odds -= abs(val) % 2;
    sweetness -= val;
  auto& best() {
    best_so_far = numeric_limits<long>::lowest();
    found_any = false;
    sweetness = 0;
    odds = 0;
    long left = 0;
    long right = 0;
    while (left < N) {
      while (unlimited() && right < N) {
      if (left < right) {
      if (right > left) {
    return *this;
int main() {
  int T;
  cin >> T;
  for (int c = 1; c <= T; ++c) {
    auto solved = MaxSweetness()
    cout << "Case #" << c << ": ";
    if (solved.found_any)
      cout << solved.best_so_far;
      cout << "IMPOSSIBLE";  
    cout << endl;
Notice how this allows to have short lines, and easily move logic into helper functions.

In an interview where time is at a premium this means you can structure code to impress without having to remember to pass a long list of arguments.

Not only does this reduce verbose, error prone code passing repetitive parameters, it greatly reduces the costs of modifying that code to add another parameter. Instead of having to thread the new parameter through a complex chain of nested calls, the value is accessible everywhere.

It's particularly handy for recursive calls where some state needs to be transmitted. The state can be made implicit in the this parameter. Of course, it doesn't work for everything. Having the code for a big project in one file becomes unwieldy and it's hard to understand what affects what. Use this tool appropriately!

Post a comment