From mboxrd@z Thu Jan 1 00:00:00 1970 To: Fans of the OS Plan 9 from Bell Labs <9fans@9fans.net> In-reply-to: Your message of "Wed, 02 Sep 2009 08:20:52 PDT." <1251904852.16936.3250.camel@work.SFBay.Sun.COM> References: <09650C1A-A4C8-4030-81D6-9AC8913970A2@kix.in> <1251904852.16936.3250.camel@work.SFBay.Sun.COM> From: Bakul Shah Date: Wed, 2 Sep 2009 10:51:21 -0700 Message-Id: <20090902175121.390BA5B3E@mail.bitblocks.com> Subject: Re: [9fans] "Blocks" in C Topicbox-Message-UUID: 5f78bbfc-ead5-11e9-9d60-3106f5b1d025 On Wed, 02 Sep 2009 08:20:52 PDT Roman V Shaposhnik wrote: > On Wed, 2009-09-02 at 10:04 +0200, Anant Narayanan wrote: > > Mac OS 10.6 introduced a new C compiler frontend (clang), which added > > support for "blocks" in C [1]. Blocks basically add closures and > > anonymous functions to C (and it's derivatives). > > They are NOT closures in my book. They lack lexical scoping. A true > closure makes the following possible (using JavaScript to stay closer > to C syntax): > function outer() { > var outer_var = 1; > return function () { > outer_var = { simply: "a different object" } > } > } >>From reading the URL you cited, it seems you can't return a block from a function but you can sort of achieve the same effect by declaring a global `block variable' and assigning a block to it -- now you can use this block var elsewhere. int (^ugly)(); int outer() { __block int outer_var = 1; ugly = ^{ outer_var = 42; } } Presumably __block says outer_var is allocated on heap so now it can live beyond the life of a particular invocation of outer(). outer_var will have to be freed when ugly is assigned to a different block. So it seems GC is a requirement now. The original C features were "just right" to keep the compiler simple and still provide a lot of expressive power. IMHO GC doesn't fit that model. Because of the heap allocation, most likely you won't get a proper closure. For instance, in Scheme (define (counter) (let ((x 0)) (lambda () (set! x (+ x 1)) x))) (define c1 (counter)) (c1) => 1 (c1) => 2 (define c2 (counter)) (c2) => 1 (c1) => 3 etc. Thus on every invocation of the counter function you get a fresh counter x and c1 and c2 increment their own copy of x independently. With blocks you'd render the above as something like: typedef int(^ctr_t)(); int counter(ctr_t*c) { __block int x = 0; *c = ^{ return ++x; } } ctr_t c1, c2; void foo() { counter(&c1); // presumably taking address of a block var is allowed printf("%d\n", c1()); printf("%d\n", c1()); counter(&c2); printf("%d\n", c2()); printf("%d\n", c1()); } I bet you'd get 1 2 3 4, and not 1 2 1 3. If they do get this right, they'd have to allocate a fresh x on every invocation of counter and this will add to the memory garbage. Now their GC problem is even worse! And I haven't even used concurrency so far! They very carefully delineate what you can and can not do with lexically scoped variables etc. but the model is far more complex. > > How much effort would it be to support a feature similar to blocks in > > 8c (and family)? What are your thoughts on the idea in general? > > Personally I think you'd be better off exploring a connection that a > language called Lua has to C. In the immortal words of Casablanca it > just could be "the begging of a beautiful friendship". Amen! Or kill one's prejudice against parens (talk to your parens!) and use Scheme which is about as simple as it can get.