Blocks: minimal or multiple kinds?

# Herby Vojčík (14 years ago)

Hello,

what is the policy and projected outcome (in spec, grammer-definition-wise as well as described-abstraction-wise) of the diiferent kinds of {...} blocks?

There is plain code block, an object-literal, module block, class block, lambda-block. Maybe I missed some use case.

My question is whether will all of these blocks presented as a separate concept, each with its own rules; or whether there is more a wish of minimal system of {...} blocks where number of different kinds/abstractions is kept to a minimum a each of aforementioned use is presented as only a use case of a more general concept.

I feel having many kinds of blocks, each having their own rules makes the language complicated and that having only a few kinds of blocks is enough.

For example, this shows two clearly distinguished abstractions/syntaxes of {...} can be enough for the whole language:

All of:

  • code block
  • module block*
  • lambda block** could be use cases of one type of block - the code (imperative) block. An imperative block should have its own structure and rules (statements, var/function declarations. automatic semicolon insertion, sub-code-blocks, ...) and above mentioned items would only be special cases of it (see footnotes). But all of them would share everything that is common for imperative block - by design. So it can be defined once and used for every case, without change.

All of:

  • object literal
  • class block*** could be use cases of one type of block - the data (descriptive) block. A descriptive block should have its own structure and rules (properties, const/non-enum/... modifiers, method definition syntax, ...) and and above mentioned items would only be special cases of it (see footnotes). But all of them would share everything that is common for declarative block - by design. So it can be defined once and used for every case, without change.

I think this would bring less confusion of what kind of construct is supported where and the borderline will be drawn very clearly (possible for future {...} constructs, as well).

Herby

  • can contain export keyword; appears in context of module keyword ** has |args| at the beginning; appears in expression context *** can contain static keyword; appears in context of class keyword
# Brendan Eich (14 years ago)

I think you are missing something important:

...; { S(); T(); }; ...

shows a block. It is eagerly evaluated in succession after the elided code to the left.

...; {|| S(); T(); }; ...

is a block-lambda, which in this example is useless -- but crucially, it is not invoked without suffixing (), as with any callable object.

Now, to make the examples more useful, suppose we could support blocks as expressions without making an ambiguous or overcomplicated grammar:

...; b = { S(); T(); }; ...

given a declared b would mean what? It should not create a callable object that must be invoked to evaluate S(); T(), because that breaks symmetry with existing block-statement meaning, e.g.

if (C) { S(); T(); }

There's no () to invoke the consequent block -- if C evaluates to true then control flows to the then clause which eagerly evaluates the block statement.

There are other problems with unifying block and object literal syntax (never mind semantics), but this one is enough to kill the idea. Blocks as braced statement lists are not a single thing in JS, even in ES1: function bodies are not block statements (consider var hoisting).

# Herby Vojčík (14 years ago)

I wasn't talking about unifying code and data blocks (here). I was pointing to the fact that there seem to be lot more (different as to syntax and as to general abstraction). I was asking if it would be possible to postulate that there are only two "higher level" kinds of blocks:

  • imperative ones (list of statements, semicolon delimited and/or structured) (this would include code block, function body, module and lambda block)
  • declarative ones (list of data productions, colon delimited and/or structured) (this would include object literal and class)

This is so the code/data line is sharply drawn (unlike your example, I proposed the opposite) and all syntactic elements and their general underlying semantics (possible statements for imperative, member modifiers for declarative, ...) be defined in one place and consistently used in all use cases.

In other words b = { S(); T(); }; would be syntax error because { S(); T(); } cannot be parsed in expression context (as it is today), the same for other examples.

My main point is to not have "hybrid" curly blocks (like class is today), but define one for code and one for data, give it rich expressivity (like allenwb is doing to object literal) and consistently define - this is code, this is data, this is code, this is data for this or that language construct.

Herby

P.S.: The other proposal I had does something like unifying them, but in completely different way, not the one in which your examples are problematic. None of them is problematic there, but it is on the other thread, anyway. Here I only wanted to here about "either code or data, nothing in between" idea.

-----Pôvodná správa---

# Brendan Eich (14 years ago)

I wasn't talking about unifying code and data blocks (here).

Ok, I must have misread. I thought you wrote "All of: - code block - module block* - lambda block** could be use cases of one type of block". I don't see how that is possible given what I wrote in reply.

Both syntax (|| is required for a block-lambda empty parameter list) and semantically (delayed evaluationg until invocation; completion reform also), block-lambdas and blocks are not "one type of block".

# Herby Vojčík (14 years ago)

Oh. I feel the barrier of misunderstanding :-(

Give me one more try to get the message through. (I'd like to have some IM or irc with you, because it would be bad to be misunderstood again)

I am trying to draw a line not on actual execution semantics (code blocks and lambda blocks are different in this), but by "inherent" nature of the block contents.

The code blocks is sequence of statements, variable declarations, function declarations, module declarations (maybe something more). Let me call them "imperative elements". If delimited, semicolon is used. The "inherent nature" of code block is to represent the linear list of actions (some of them simple, some of them structured) that specify the control flow of some code actions. I call this inherent nature "imperative". The lambda block is sequence of "imperative elements", too. In fact, except |args| at the beginning, there is not structural change that makes it different form code block. Its "inherent nature" is imperative, too (even if it is parsed as expression and not run immediately. It is not the matter here). A module block is sequence of "imperative elements, too. It can contain export keyword, but otherwise, again, no big difference, structure-wise, from the code block. And its "inherent nature" is also imperative. It is much akin to the body of <script> in web apps today (though it can be

nested).

So I am saying here - let us define an imperative block structure and elements (and the structure itself is dictated by its purpose. to represent list of "imperative elements", that is, control-flow). Then let us say the all above mentioned cases of {...} (include if/then/....sub0blocks, function bodies, and more) are all imperative blocks. The common structure and elements give common semantics of the block contents, but the blocks themselves are different from use case to use case. Nevertheless, the elements they are build from would be consistent all over.

And now, the object literal is sequence of member productions and method declarations. Let me call then "declarative elements". If delimited, colon is used. The "inherent nature" of object literal is to represent (unordered) list of data productions that specify the blueprint of a data structure. I call this inherent nature "declarative". I claim that class block's "inherent nature" is also purely declarative. It declares the structure of the class. In declaring a class, there is nothing imperative (in constuctor body, yes, but it is a function body, it is of course, imperative).

I wanted to draw a line, much more "abstraction-wise", using the "inherent nature" or "purpose" to define imperative blocks and declarative blocks, and to postulate that every {...} has only one of such natures. Not mixing them.

The good things it can bring is consistent use of the same metaphor and same building blocks with same meaning all over (code - list of statements etc. same in all contexts; data - list of members, having access modifiers, possibility to declare a method, ... exactly same all over); "clarity" or how should I call it - the well-defined intent-separated case of use of {...} and last but not least, much greater possibility to enhance their expressive power if there will be less concerns that hybrids will exist.

In no way did I want to propose that { code } be taken as expression. On the contrary - each use of {...} is well categorized as either code or data, and its structure is then given by that.

Herby

P.S.: Do not take me by the word - I know the class needs to describe constructor function's members as well as prototype's member. But it has static keyword for it. Everything in the class which is not static is the description of the class' prototype. And if it is declarative, it should be written as declarative (object-literal way). imnsho. (if class is just a use case of "declarative" block group, then it get all the goodies automatically and they would be guaranteed to work same way as they do for object literal (I mean access modifiers, method declaration syntax, and there can be more). No special cases (except static) which is in one place but not in another.

P.P.S.: I am not disclosing that this was created in my mind while I was working on that other thing (which is in "RFC: Empowered data" thread") but a) I see how powerful vistas does this open b) I (by feeling only) think that overall having such clear imperative / declarative distinction on one side and consistent use of building elements within each group is a Good Thing (tm). (as I am writing it, it associated me "loose coupling" and "high cohesion").

-----Pôvodná správa---