Hacker News new | past | comments | ask | show | jobs | submit login
Lambdas in C (walfield.org)
111 points by wingo on July 1, 2011 | hide | past | web | favorite | 38 comments

In other words - he discovered gcc's ({ }) construct and decided to write a macros named lambda around it.


To me it's a bit more surprising: it's a statement expression, containing a nested function, then we take the address of that nested function.

Having that series of operations produce something useful is unexpected, to say the least.

It's nothing I'd want to see in code that I maintain, but I think you have to give it a bit more credit. Besides that, it's cute :)

Any idea if this is now officially supported in GCC? The blog post is from nearly a year ago.

It's been officially supported in GCC since about a decade ago, I believe. It's not part of the C language, so if you're writing this code, it's no longer C. It also requires executable stacks.

The article indicates its not officially supported, although it could be.

     Ian Lance Taylor replied that the behavior is 
     undefined, but so far stable. His suggestion was 
     to open a bug and request that this desirable (!!!)
     behavior be explicitly permitted.

Oh. Right, because of the new scope that the anonymous block creates. My mistake.

why does it requires executable stacks?

Because of the way nested functions are implemented in GCC.

If you call a nested function it has to know the frame address of the containing function in order to refer to its local variables. So if you take the address of such a function the program actually receives the address of a "trampoline": a small sequence of instructions which first loads the frame address and then calls the real function.

The trampoline itself is generated dynamically and putting it on the stack turns out to be cheaper than heap allocating it.

> and putting it on the stack turns out to be cheaper than heap allocating it.

Well, more so that C has no concept of the heap. That's entirely contained within the C libraries. So, the language implementation itself can't do heap allocations (at least, not without sacrificing any chance of using it in embedded or kernel dev situations where you don't have heap allocation, or where you need to use custom allocators)

If you're using closures in an embedded environment with no heap, you've got bigger problems than executable stacks to worry about..

Right. And of course the whole idea is prone to insane amounts of fail.

    > Thus, if you access the enclosing environment, you
    > must ensure that the lifetime of this lambda is bound by the
    > lifetime of the enclosing environment (i.e., until the enclosing
    > function returns).  This means that if you access local
    > variables, bad things will happen.  If you don't access local
    > variables, you're fine.
Might as well avoid the complications, hoist the function and pass a function pointer with some context (assuming it's needed).


You're not passing a compound statement expression as a function pointer. You are passing the result of the evaluation of that expression --which is a function pointer -- as a function pointer.

Oops, sorry, I just deleted my comment because I thought that wingo's comment covered my thoughts and more. For posterity, I had previously written:

I do not find it obvious from that documentation that you can pass a compound statement expression as a function pointer.

And touche', I just glanced at the contents of the macro before replying.

Blocks, http://clang.llvm.org/docs/BlockLanguageSpec.txt

Much better solution to the problem, hopefully coming to standard GCC soon(tm).

They should try to make something syntactically compatible with C++ lambdas. It's annoying that the two languages are more and more incompatible for cosmetic reasons.

Am I really out of my mind or is this not a lambda at all but just an anonymous function?

If an anonymous function can capture state from the enclosing code block, and outlive that code block, then it's a lambda.

So this probably isn't really a lambda.

That's the definition of a closure. I'm not sure if "lambda" is necessarily synonymous with "closure" or just synonymous with "anonymous function", but lambdas are closures in many languages.

You're right, my terms are sloppy. Are there rigorous definitions, or does it vary between languages?.

I think of "lambda" as a language construct that creates closures. "anonymous functions" are of course just a simpler subset of closures.

Note: unlike lambdas in functional languages, this lambda does not capture the containing environment.

That's slightly sloppy phrasing. It captures it, but it does not duplicate it, so it's lifetime is tied to the stack frame of the function you declared it in.

In short, you can only do downwards funargs.

Bah, beat me to it. It's still kind of cool though.

    qsort (array, sizeof (array) / sizeof (array[0]), sizeof (array[0]),
         lambda (int, (const void *a, const void *b),
                   dump ();
                   printf ("Comparison %d: %d and %d\n",
                           ++ comparison, *(const int *) a, *(const int *) b);
                   return *(const int *) a - *(const int *) b;

My favorite part: l_anonymous_functions_name

Sorry, I just like the irony.

Cute, but functions pointers suit me just fine.

Anonymous functions are very handy, and I think they should really be taken a step further. There's really no reason why it shouldn't be possible to pass an actual function body where a function pointer is required:

  int v[...];

  qsort( v, ..., (a, b) { return *(int*)a - *(int*)b } );
Such usage would also have an added benefit of letting the compiler do the type inference for a, b and the return value and not needing to explicitly specify them in qsort() call.

Local functions would be a nice compromise between the unreadable glop that was posted and the need to declare an entirely separate function to execute localized actions.

I have not been able to work out two things here:

1) If I define a lambda inside a function, I cannot return the lambda from the function and use it right?

2) Recursive lambdas are not possible on account of the lack of recursion in C macros (not sure if that even makes sense without 1).

Isn't this a step closer to Greenspuns Tenth Rule?

"Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of CommonLisp"

(bar the environment/scope issue it avoids that is)

More accurate title would be "Something like, but not as useful or generic as, lambdas in gcc's dialect of C".

One might say, to paraphrase Douglas Adams, that it's almost, but not quite, entirely unlike lambdas.

If I worked on static code analysis tools for a living, this would make me cry.

  printf("I am bad and evil %p\n", ({int bad_and_evil(void){return 0;};&bad_and_evil;}));
  printf("I am bad and evil2 %d\n", ({0x4FFFFFF;}));
  void* result = ({void* bad_and_evil(int dummy){return 0;};&bad_and_evil;});
  printf("bad and evil 3 = %p\n", result);
* shudder * Getting a return value from a block, by inserting it in parantheses.

This is just too clumsy, and this is exactly why I think go will take off.

  package main
  import "fmt"
  func main() {
      add := func(x int, y int) int{
        return x+y
      max := func(x int, y int) int{
        if x>y {return x}
        return y
    fmt.Printf("lambda: add:%d\n",add(2,3))
    fmt.Printf("lambda: max:%d\n",max(2,3))
Although it'd be nice if you could have a ternary or something like

  return {x} if x>y else {y}

Is that really that much better than C blocks?

    #include <stdio.h>
    int main (int argc, char const *argv[]) {
        int (^add)(int, int) = ^(int x, int y) {
            return x + y;
        int (^max)(int, int) = ^(int x, int y) {
            return (x > y) ? x : y;
        printf("block: add:%d\n", add(2,3));
        printf("block: max:%d\n", max(2,3));
        return 0;

I personally think so, as I think blocks in C are a clumsy way around the issue (probably because C doesn't natively have garbage collection) and it feels natural in go, but I know others will disagree.

Also, I don't think you can make it anonymous, can you?

  func(x int, y int){fmt.Printf("%d\n",x+y)}(2,4)

You sure can.

   ^(int x, int y){printf("%d\n",x+y);}(2,4);
Automatic memory management is interesting, but orthogonal.

also, this works too:

  val := func(x int, y int) int{return x+y}(2,4)

Nice, but how is this useful though?

Registration is open for Startup School 2019. Classes start July 22nd.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact