A lambda is not necessarily a closure
wingolog.org
wingolog.org
I don't think this is right, unless given an unnecessarily strict definition of `well-known`. A contrived counterexample:
let example x y =
let f =
let z = big_calculation x in
(fun t -> if t then z else 0) in
(fun q -> f q + f y)
Here f is bound to a known function, which is only called and never escapes. All of the known-function optimisations should apply. However the variable z is not in scope at the call sites of f.Optimising this example effectively is a bit tricky - the 'standard' thing to do is construct a closure that contains z (but need not contain a code pointer, since all call sites are known), which is itself contained within the closure for (fun q -> ...). A more aggressive method would be to discover that all references to f are within (fun q -> ...) and inline the free variables of f into its closure, avoiding an indirection.
It's quite a fun subject - there are various implementation tricks for curried functions and partial application too, which aren't very important in Scheme but are crucial for ML.
http://scottmeyers.blogspot.co.uk/2013/05/lambdas-vs-closure...
The relationship between
lambda and closure
is like between class and instance
or type and data
.This goes against the usage of "closure" that I've seen elsewhere, including the current wikipedia page for the topic[1]. Ususally I see "closure" used to denote the concept of a function that references free variables, not any specific implementation. Maybe within the guile codebase they have a construct called "closure" which implements closures in the most general way possible (which involves heap allocation no doubt).
But it's true: lambdas ≠ closures. "Lambda" refers to the syntax that a language uses to allow anonymous functions. A "closure" is a type of function that references free variables. Java 8, for example, has lambdas, but they are not full closures, because they can only reference local variables declared in higher scopes if those are declared as final.
Python, on the other hand, sort of has lambdas, but they are limited to a body consisting of a single expression, which doesn't allow e.g. conditionals. Even if it didn't support the lambda syntax, though, it would support closures, because it lets you declare a named function during a function call, then pass it elsewhere by referencing its name. These functions can reference free variables:
def mkincer():
n = 0
def inner():
nonlocal n
n += 1
return n
return inner
[1] https://en.wikipedia.org/wiki/Closure_(computer_programming)Effectively final (a variable or parameter whose value is never changed after it is initialized)
In programming languages, closures (also lexical closures
or function closures) are a technique for implementing
lexically scoped name binding in languages with first-
class functions.
which is the same definition that wingo is using. Stack allocation is another technique for implementing lexical scope, if the relevant bindings are still on the stack.One of the things that makes lambda so powerful is that it does not force a particular implementation on the language implementer, and allows for plenty of room for optimization.
Seriously, people. At least learn the abstractions that your using.
Very cool to read about these kinds of compiler optimizations. The "scheme class" I've taken was more about programming techniques that scheme encourages (tail recursion, continuation passing) than specific details about the language itself. Im going to have to go read more about the partial evaluation the Guile compiler is performing, I'm curious what heuristics it uses for the definition of "evaluateable".
(Does anyone write non-strict JS these days? Why?)
I am not a lisp person but thinking of this in terms of say C# or Haskell.
I'm not overly fond of calling anonymous functions "lambdas", for that matter. Sure, several languages use the term lambda, but many do not. More specifically, "anonymous function" is much more descriptive than "lambda".
Similarly, people refer to constructor functions that always return objects of the same class/prototype as factories, despite both the GoF patterns that popularised the term showing dynamic output. IIRC Angular JS misuses the term this way too.
AFAICT it's either genuine ignorance of how/why these words describe or some kind of weird demonstration of cleverness by using a particular term, regardless of whether it's applicable or not.
Also, there are some specific optimisations, like beta-reduction and closure inlining.
Floating Functions / First class functions (thanks to comments for reminding): Functions that can be passed around as variables. e.g. All functions in Javascript are floating functions.
function callback() { ... }
// function assigned to a variable
var x = callback;
// function is an argument to another function
doSomething(..., callback)
Anonymous functions: functions with no name. // an anonymous function
(function() { .... })()
// anonymous function as an argument
doSomething(..., function() { .... })
Lambda functions: A style of writing functions without a block, where the function and return value are expressed as one single expression without the usage of a block or explicit returns. employee => employee.name
Closures: When a function references a variable outside its scope, and that "frees" the variable from its normal lifetime, thus creating a "closure" between the function and the now-free variable. function a() {
var x = 1;
return function() {
// here a closure is created
// now "x" outlives its lifetime and
// becomes a free variable
return x + 1;
}
}I would argue the true distinguishing feature of a "lambda function" in a PL is that it is first-class, nest-able, and lexically-scoped (that is, it captures bindings from its definition site).
A closure is simply a way of implementing lexical scope for functions.
A variable is a "free variable" not because it is "freed" from its lifetime, but because it is not bound by its enclosing lambda. (It would still be a free variable even if the function and the variable binding had the same lifetime.)
This is not what a lambda is.
? (defun closure-test ()
(let ((x 32))
(list (lambda (y) (+ y 10))
(lambda (y) (+ x y)))))
CLOSURE-TEST
? (closure-test)
(#<Compiled-function (:INTERNAL CLOSURE-TEST) (Non-Global) #x302000CBD71F>
#<COMPILED-LEXICAL-CLOSURE (:INTERNAL CLOSURE-TEST) #x302000D1EE2F>)Some Lisps use an Interpreter for interactive use in the Listener and the compiler for the rest.
It's a typical tradeoff.
something.unwrap_or_else(|err| {
writeln!(io::stderr(), "{}", err);
process::exit(1);
}
This is really just a function call. There are no references to an outer scope. But Rust requires a closure there. You can't pass a pointer to a function.You'd like for this to optimize down to a function call, without saving any state from the outer scope. Don't know if Rust does this yet.
Actually, you can.
fn perr(e: &str) {
println!("There was an error :(, {}", e)
}
fn main() {
Err("ERROR").unwrap_or_else(perr);
}
The above code works, and can be run here: http://is.gd/2RCmG9Post "unboxed closures", closures are now stored on the stack (thus they usually optimize away; for example if you use an iterator with a bunch of adapters like filter/map it will optimize down to a for loop). Each closure has a unique anonymous type (you can box them if you want them to have the same type), and all closures implement one or more of the Fn* traits, as do function pointers. So arguments can be generic over all callables, and you can pass in a closure or a function or your own custom callable.
Also, functions could coerce to either of the closure types, and hence be passed straight to functions expecting them.
Huh, I recall this not working. Perhaps I made a mistake then.
> closures were pointers into the stack
Ah, the type erasure was through &mut. TIL; I always thought it was Box only.
fn main() {
Err("ERROR").unwrap_or_else(perr);
}gross.
My (limited) understanding is that all bindings closed over by a closure are represented as anonymous struct passed to the function, if nothing is closed over that will be a unit struct which will of course be optimized to nothing.
Rust is very good at optimizing away closures to have as little overhead as is possible for the semantics they describe.
And, you can pass a pointer to a function just fine:
fn call_twice<F>(f: F) where F: Fn() {
f();
f();
}
fn hello() {
println!("Hello");
}
fn main() {
call_twice(|| println!("Hi!"));
call_twice(hello);
let x = 5;
call_twice(|| println!("{}", x));
}
And all of this optimizes down just fine; if the environment being closed over is empty, it will just be an anonymous function with no extra overhead. Heck, if you try it out yourself on the playpen in release mode, and take a look at the asm, all of these just get inlined right into main: http://is.gd/p9Kd9yBy the way, function pointers have been able to be passed directly where closures, both the new and old forms, are expected since at least 0.13.
It does. Rust closures translate to a stack struct containing the capture and a method call on that struct. In this case, the struct is zero-sized, so it doesn't exist, so it's just a function call. Additionally, unwrap_or_else will probably be monomorphised and then inlined, so there will be no call at all.
"closure" is just the term for the syntax.
> You can't pass a pointer to a function.
Sure you can.
fn main() {
Ok(1).unwrap_or_else(inc);
}
fn inc(x: u8) -> u8 {
x+1
}
Both functions and closures implement the Fn* traitshttps://d.mort.coffee/img/change-width.png
That inspector is incredibly useful any time a website does something stupid and you just want to read the bloody content.
Confusing lambda expressions with closures is like confusing the string "new Foo()" with the object it constructs. In each case, the former is a syntactic entity, the latter an implementation construct.
int xs[10];
Is this a function? int f(int x) { return 2*x; }
I think it's overly pedantic to require people to say "function defined by this code".To a language user, certainly, such distinctions usually don't matter. But conflating "lambda expression" and "closure" is particularly problematic. On the one hand, as this article points out, not all lambda expressions are evaluated by constructing closures. On the other, it's certainly not the case that all closures come from lambda expressions -- in many languages (certainly Scheme, Common Lisp, JavaScript, and Python, and I think Ruby and PHP) you can perfectly well create a closure from a named function; it's done all the time. Here's an example in Python:
def f(a):
def g(b):
return a + b
return g
See? f returns a closure, but there's no lambda expression in the code.So given that lambda expressions and closures, though related, are not in 1-to-1 correspondence, I think it's important to understand the distinction.
(lambda (x) (+ x 1)) doesn't close over anything, so it's not a closure, nor does it evaluate to one. For that matter, (let ((x 0)) (lambda (x) (+ x 1))) isn't really a closure in any sense because the environment can be inlined out.
But it's pointless to say that "the source code that, when evaluated, creates a named function, is itself not a function" is just pointless lawyerism.