News for hackers. Stuff that matters.
Re:Closures and Objects Are Equivalent (2013) by IshKebab on Sunday November 20, 2022 @04:29AM (#33679591)
Honestly both names are really terrible. It took me an embarrassing amount of time to understand what a closure is just because "closure" and "closing over" are hilariously obtuse names for what is a very simple concept.

It would be clearer if people just called them function objects. They are effectively the same thing, just with nicer syntax for declaring them. Or even just "anonymous functions". I'm not sure the fact that they have data automatically attached to them is really enough to warrant a completely different and confusing name.

Re:Closures and Objects Are Equivalent (2013) by int_19h on Sunday November 20, 2022 @05:10AM (#33679769)
> I'm not sure the fact that they have data automatically attached to them is really enough to warrant a completely different and confusing name.

It is, since the lifetime implications are profound - either in form of dangling pointers, or in form of local variables outliving their execution frame.

Re:Closures and Objects Are Equivalent (2013) by IshKebab on Sunday November 20, 2022 @07:51AM (#33680517)
Nope, the lifetime implications are a completely separate dimension. You can still have closures that don't retain any reference to variables with different lifetimes, e.g. using `[=]` in C++ or `move` in Rust.

Also, we don't have a separate name for structs that contain references/pointers and structs that don't do we?

Re:Closures and Objects Are Equivalent (2013) by morelisp on Sunday November 20, 2022 @08:42AM (#33680788)
> Also, we don't have a separate name for structs that contain references/pointers and structs that don't do we?

Languages name things they need to help their implementation. In Go, such types are named ("pointer-ful" and "pointer-free"), because they are allocated differently to assist GC. In C++, they're not but instead we have e.g. PODs which other languages don't need to name, because of other optimizations C++ wants to do.

Re:Closures and Objects Are Equivalent (2013) by int_19h on Sunday November 20, 2022 @05:00PM (#33685886)
We don't because those pointers are explicit. But they're not with closures, even with something as low-level as [&] in C++.

And, yes, what [=] produces is not a closure, by definition (but it's still a lambda).

Re:Closures and Objects Are Equivalent (2013) by gpderetta on Tuesday November 22, 2022 @06:12AM (#33704493)
Sorry, why wouldn't closing over a variable by value count as a closure? In fact in many functional languages you can't tell the difference.
Re:Closures and Objects Are Equivalent (2013) by int_19h on Tuesday November 22, 2022 @05:46PM (#33712638)
In functional languages where you can't tell the difference, like ML or Haskell, values are bound directly to variables, so capturing the value is closing over the variable. But in C++, variables represent storage locations which contain values, so if you copy the value, you're not really closing over the variable (unless it's const).

To look at it from another perspective, if a closure closes over the variable, then references to that variable inside the closure should behave the same as references to that variable outside of it. If you can change the variable value outside, and it's not reflected inside the closure, then it's not really the same variable inside and outside.

Re:Closures and Objects Are Equivalent (2013) by morelisp on Sunday November 20, 2022 @06:44AM (#33680181)
The name is unfortunate because it's distinct from the other meaning of "closure" in mathematics (e.g. as in "transitive closure") but it's definitely not captured by "function objects" nor by "anonymous functions". Function objects may close over nothing, either by virtue of being pure functions or by having unbound variables resolved in dynamic scope. Similarly, they're not always anonymous, and anonymous functions are just as often closing over nothing (if not more often - lots of trivial map/filter/reduce are pure).
Re:Closures and Objects Are Equivalent (2013) by IshKebab on Sunday November 20, 2022 @07:56AM (#33680546)
> Function objects may close over nothing

So you're saying a closure that happens to not close over any variables is no longer a closure? I don't think many programming languages agree with that. For instance C++: https://en.cppreference.com/w/cpp/language/lambda or Rust: https://doc.rust-lang.org/book/ch13-01-closures.html

They both use "closure" in all cases even if the function doesn't actually bind any scope variables.

We don't have a special name for functions that take 0 parameters. I don't think a special name is warranted here either.

Re:Closures and Objects Are Equivalent (2013) by morelisp on Sunday November 20, 2022 @08:14AM (#33680663)
Not sure what you're saying, Rust specifically calls out the degenerate case of "closures" as having very different behavior:

Non-capturing closures are closures that don't capture anything from their environment. They can be coerced to function pointers (e.g., fn()) with the matching signature.

There's value in deference to practicality - we want to treat n-ary closures and n-ary functions the same way much of the time so we should make it easy to write functions which accept both which means they should be nameable via the same succint type - but we also shouldn't confuse that practicality for formalism.

> We don't have a special name for functions that take 0 parameters.

Only because we have generalized "nullary", "unary", etc. We know that e.g. a nullary pure function must also be a constant function. There are similar useful implications we can draw for "non-capturing closures" that makes them a distinct type in the formal sense, even if not in the "this is the type a particular language attaches to the resulting variable/value" sense.

But what does any of this have to do with your original point? I don't think anyone claimed closures aren't a kind of function, only that "function objects" is not synonymous with them. Most languages, including obviously Rust and C++, have functions objects which aren't closures

Re:Closures and Objects Are Equivalent (2013) by wruza on Sunday November 20, 2022 @07:01AM (#33680268)
“Anonymous function” simply means no name, that would be equally confusing. For “function objects”, closures may be more than just a nice syntax for it. E.g. two closures may share a symbol:

  let x
  fn setx(v) {x = v}
  fn getx() {return x}
And that symbol still lives in scope:

  x = 5; getx() // 5
It may be not the best name, but these nuances beg for a distinct one.
Re:Closures and Objects Are Equivalent (2013) by IshKebab on Sunday November 20, 2022 @07:49AM (#33680508)
> For “function objects”, closures may be more than just a nice syntax for it.

Not in any language I've used. I'm not sure what language you've used but in C++ you would have written:

  int x;
  const auto getx = [&]() { return x; };
  x = 5;
  getx(); // 5
But that's exactly the same as this:

  int x;
  struct GetxFunctionObject {
    int& x;
    int operator()() { return x; }
  };
  const auto getx = GetxFunctionObject { x };
  getx(); // 5
(Something like that anyway; it's been a while since I wrote C++)
Re:Closures and Objects Are Equivalent (2013) by magicalhippo on Sunday November 20, 2022 @09:01AM (#33680939)
In Delphi, closures are implemented pretty much exactly as your second variant. They're implemented using a compiler-generated class.

A difference is that you can't specify capture type, all captured variables are stored as fields in the class, and any outer references (such as x here) are replaced to use the field in the instance:

    struct GetxFunctionObject {
      int x;
      int operator()() { return x; }
    };
    const auto getx = GetxFunctionObject();
    getx.x = 5; // compiler automatically changes "x = 5" to this
    getx(); // 5
Modulo any it's-too-early-in-the-morning-to-write-C++ mistakes I made.

I can't recall the details if multiple closures captures the same variable, might be the "obvious" solution of a shared data-class instance.