Hacker News new | comments | show | ask | jobs | submit login

I'm confused, aren't blocks just a hack around a lack of first class functions and/or closures?

The actual complaint seems to be that JavaScript's scoping rules are different to his expectations and 'this' binding is, well, we know.




No, blocks are not a hack around first-class functions. Let’s talk about “what we’re making first-class.” A first-class function is something we both understand: A thingy with its own variable scope, its own notion of “this,” some parameters, and some executable code that may or may not return a result.

So what is a block? That’s easy in JavaScript. Here’s a block:

  if (foo === bar)
  // The block starts here v
  {
    return foo;
  }
  // The block ends here ^
Blocks are the chunks of executable code living inside of a function. They aren’t functions! They share their enclosing function’s scope. They share their enclosing function’s notion of “this”. You can’t “return” from a block, if you execute a return form a block, you return from the surrounding function.

Blocks already exist in JavaScript, but at the moment they only exist for built-in keyword construct like “if” and “for." Yahuda is simply explaining why it would be valuable to create a way to pass blocks to functions. The blocks would continue to be associated with their enclosing function invocation, unlike passing a function.

To summarize, JavaScript already has first-class functions, and it already has blocks, this proposal concerns a way to make blocks first-class. Blocks are not a hack around first-class functions, they’re something else that JavaScript already has.

-----


Actually, while it's almost never used (I certainly haven't seen code using it), blocks cooperate with labels too. The following is perfectly valid:

  var fn = function() {
      nowWeDance: {
          dance();
      }
  };
The block uses the same execution context and scope, of course, so it's not useful.

A block is legal alone too:

  (function() {
      {
          return 1;
      }
  })(); // 1
I suppose for a very long switch statement (god forbid), blocks could be useful:

  switch (x) {
      case 1: {
          // do stuff
          break;
      }
      case 2: {
          // do stuff
          break;
      }
  }
But I wouldn't favor it because it makes the break seem implicit to the reader when it in fact is not.

-----


You can actually cause a block to return control to it's invoker (the equiv of return in JS) by using the 'next' keyword.

-----


He's not proposing to make blocks first-class! That would mean you'd be able to assign blocks to variables, and that makes little sense. Blocks would remain special non-first-class entities.

-----


No, he isn’t proposing that blocks be entirely first class. Although... Why not?

  foo ? bar.map { |x| x * x } : bar.map { |x| x + x }
Could be:

  bar.map foo ? { |x| x * x } : { |x| x + x }
Perhaps this is too much or too Ruby-ish:

  function either (pred) {
    return pred ? { |x| x * x } : { |x| x + x };
  }

  bar.map(either(foo))

-----


Consider the following:

    function map(a, cc) {
        r = []
        a.each{|e| r.push( cc(e) ) }
        return r
    }
Now `cc` is some kind of callable. It might be a function or it might be a block.

Suppose `cc` is a block defined as cc = {|e| return e }

Does this mean the `return` in `cc` breaks out of the outer each-loop because it's a block as well, or does the return simply do the normal thing?

In the first case you need case analysis every time you write a higher order function in order to make sure the code flow makes sense. This is unacceptable.

The alternative is that the "return e" in `cc` behaves like it would in a lambda function (i.e. the map function works correctly). But that means that when you assign a block to a variable you change its semantic meaning. Also completely unacceptable.

I see absolutely no way to make first-order blocks work.

-----


In the spirit of brainstorming, I think it’s cool to come up with some cases and ask, “what if?” In Yehuda’s article, he gives:

  withFile: function(block) {
    try {
      var f = File.open(this.name, "r");
      block(f);
    } finally {
      f.close();
    }
  }
This is interesting: “block” appears to be a “callable” object. How does the code know whether it will be a block or a function? Can you assign the block parameter to a variable? Pass it to another function? What about:

  function firstClass (block) { return block; }
Can I do this? If so:

  var thisIsaBlock = firstClass { |x| return x; }
And we’re off the rails. Or worse:

  (function () {

    var local = ‘0xDEADBEEF';

    return firstClass { || local }
  })()()
If I am reading the code example correctly and my inference holds, then either (a) you have to do a lot of escape analysis to make this work, or (b) you get lazy and allow programmers to pass blocks around but emit an exception if you try to call a block once its lambda environment has already returned.

I guess I need to read the whole proposal... I am sure this has been addressed.

-----


> I'm confused, aren't blocks just a hack around a lack of first class functions and/or closures?

Not in general. In Ruby the case is more complex because Ruby blows, but if you take Smalltalk's blocks they implement closures (in Dolphin, they're instances of `BlockClosure`) and they're first-class functions.

The way in which colloquial "blocks" differ from "lambdas" first-class functions is control flow in the semantics of return: lambdas use local returns, a `return` will break from the current dynamic scope (discarding the current stack frame), whereas blocks use non-local returns: a `return` will break from an enclosing lexical scope (generally a method and you can't nest methods).

You could very well have both behaviors with the same function type, using different return operators (yehuda is full of crap when he asserts that you need both lambdas and blocks by the way, even more so since Smalltalk did not have lambdas: Smalltalk has methods and blocks, methods are not lambdas as they can't be anonymous or nested, they're a top-level construct of the class) just as you can already emulate non-local returns via exceptions today (that's basically what a non-local return is)

-----


Just to clarify, here's my original comment:

In order to have a language with return (and possibly super and other similar keywords) that satisfies the correspondence principle, the language must, like Ruby and Smalltalk before it, have a function lambda and a block lambda.

In the context of my article, "function lambda" means function in JavaScript and method in Ruby and Smalltalk. My argument is that you need a construct you can return from, and a separate construct (a "block") that can be nested in that construct.

-----


> My argument is that you need a construct you can return from, and a separate construct (a "block") that can be nested in that construct.

And that's not true, you can have a single construct and two different returns (one local and one non-local) instead.

-----


Thanks for the succinct explanation. I can see why that'd be useful for some constructs.

-----


> I can see why that'd be useful for some constructs.

It's most useful in getting rid of "C-style blocks" constructs (by which I mean non first-class blocks), which Smalltalk did quite beautifully.

-----


Sorta... If they are a hack around anything, it would be the lack of the intersection between first class lambas, closures, continuations, dynamic scoping, and macros.

In a Lisp-like language with all of those features, you could define arbitrary non-local return points by defining a macro which uses dynamic-scoping to introduce a `return` function which captures the current continuation. One argument would be a lambda/closure that would be evaluated in this new scope: capable of calling `return`.

-----




Applications are open for YC Summer 2016

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

Search: