Jamie Zawinski uses that term in his (1997) article "java sucks" as if you should know what it means:
I really hate the lack of downward-funargs; anonymous classes are a lame substitute. (I can live without long-lived closures, but I find lack of function pointers a huge pain.)
It seems to be Lisper's slang, and I could find the following brief definition here, but somehow, I think I still don't get it:
Many closures are used only during the extent of the bindings they refer to; these are known as "downward funargs" in Lisp parlance.
Were it not for Steve Yegge, I'd just feel stupid now, but it seems, it might be OK to ask:
Jamie Zawinski is a hero. A living legend. [...] A guy who can use the term "downward funargs" and then glare at you just daring you to ask him to explain it, you cretin.
-- XEmacs is dead, long live XEmacs
So is there a Lisper here who can compile this for C-style-programmers like me?
Downward funargs are local functions that are not returned or otherwise leave their declaration scope. They only can be passed downwards to other functions from the current scope.
Two examples. This is a downward funarg:
function () {
var a = 42;
var f = function () { return a + 1; }
foo(f); // `foo` is a function declared somewhere else.
}
While this is not:
function () {
var a = 42;
var f = function () { return a + 1; }
return f;
}
To better understand where the term comes from, you need to know some history.
The reason why an old Lisp hacker might distinguish downward funargs from funargs in general is that downward funargs are easy to implement in a traditional Lisp that lacks lexical variables, whereas the general case is hard.
Traditionally a local variable was implemented in a Lisp interpreter by adding a binding (the symbol name of the variable, paired with its value) to the environment. Such an environment was simple to implement using an association list. Each function had its own environment, and a pointer to the environment of the parent function. A variable reference was resolved by looking in the current environment, and if not found there, then in the parent environment, and so on up the stack of environments until the global environment was reached.
In such an implementation, local variables shadow global variables with the same name. For example, in Emacs Lisp, print-length
is a global variable that specifies the maximum length of list to print before abbreviating it. By binding this variable around the call to a function you can change the behaviour of print statements within that function:
(defun foo () (print '(1 2 3 4 5 6))) ; output depends on the value of print-length (foo) ; use global value of print-length ==> (1 2 3 4 5 6) (let ((print-length 3)) (foo)) ; bind print-length locally around the call to foo. ==> (1 2 3 ...)
You can see that in such an implementation, downward funargs are really easy to implement, because variables that are in the environment of the function when it's created will still be in the environment of the function when it's evaluated.
Variables that act like this are called special or dynamic variables, and you can create them in Common Lisp using the special
declaration.
In Common Lisp:
(let ((a 3))
(mapcar (lambda (b) (+ a b))
(list 1 2 3 4)))
-> (4 5 6 7)
In above form the lambda function is passed DOWNWARD. When called by the higher-order function MAPCAR (which gets a function and a list of values as arguments, and then applies the function to each element of the list and returns a list of the results), the lambda function still refers to the variable 'a' from the LET expression. But it happens all within the LET expression.
Compare above with this version:
(mapcar (let ((a 3))
(lambda (b) (+ a b)))
(list 1 2 3 4))
Here the lambda function is returned from the LET. UPWARD a bit. It then gets passed to the MAPCAR. When MAPCAR calls the lambda function, its surrounding LET is no longer executing - still the function needs to reference the variable 'a' from the LET.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With