Ecma262: eliminate Lexical Environment?

Created on 11 Mar 2019  路  10Comments  路  Source: tc39/ecma262

I'm thinking the spec might be better off if we eliminated the 'Lexical Environment' structure.

"A Lexical Environment consists of an Environment Record and a possibly null reference to an outer Lexical Environment."

Instead, we could make [[Outer]] a field common to all Environment Records, and then replace every Lexical Environment with the Environment Record that it refers to.

Reasons to eliminate:

  1. The name similarity between "Lexical Environment" and "Environment Record" is a possible source of confusion.

  2. A Lexical Environment is a thing with two components, rather than a record with two fields. Thus, we can't use dot-notation to express "the EnvironmentRecord component of ". We could redefine it as a record, but then we'd have a Lexical Environment record which isn't a kind of Environment Record, an even greater source of confusion.

  3. It's an extra level of indirection. Whenever we want to do something with a Lexical Environment, we typically have to add another step to extract its Environment Record component.

(Note that, due to 2 and 3, there's already the temptation to pretend it doesn't exist. E.g., PRs #702 and #1463 are considering using the phrase realm.[[GlobalEnv]].[[GlobalThisValue]]. Technically, this is incorrect, because realm.[[GlobalEnv]] is a Lexical Environment, and you have to get its Environment Record (a global ER) before you can then get the latter's [[GlobalThisValue]] field.)

Reasons to keep:

I can't see any. Layering? Tradition? Nobody wants to perform the necessary edits?

(As far as I can tell, a Lexical Environment's EnvironmentRecord component is only ever set once, when the Lexical Environment is created. Had that not been the case, that might have been a reason to keep the extra level of indirection.)

Most helpful comment

You could combine them into a single abstraction, but that blurs the important distinction between their semantics

I understand the distinction, but I don't understand why that distinction is thought to be important. E.g., is there somewhere you can point to and say "Here's where the spec benefits from making that distinction"?

and creates other irregularities. For example, every such combined abstraction would have an [[Outer]] field

Yup, and for a global environment it'd be *null*, like now.

and nesting operations

(I guess you mean the NewFooEnvironment() operations, as they're the ones that set up the nesting.)

even though some uses of the the abstraction (such as the subparts of a Global Environment Record) never use them.

Huh? Those things would be used exactly as much after the suggested merge as they are currently. Why is that an irregularity?


I'm skeptical of the value of changes such of these unless they are motivated by the need to introduce a new feature into the language.

Whereas I think the spec has been improved by changes that didn't have that motivation.

Otherwise they are just churn that carry the risk of unintentional semantic change and require established user of the spec. to relearn how to interpret parts of the specification. They also risk invalidate portions of external commentaries or books that attempt to educate readers about the ES spec.

(Note that the commentary you linked to doesn't even mention Lexical Environments or Environment Records. However, I grant that such commentaries and books exist.) Yes, those are risks of change. But it's not all doom and gloom. Refactoring can also:

  • uncover spec bugs.
  • make it easier for people to read + understand the spec.
  • make it easier for commentaries to explain the spec.

The editors have to weigh the pros and cons of each case.

I wonder if such efforts wouldn't be better spent on contributing commentary to the TC39 rationale project.

That link is a 404 for me, so I can't spend my efforts there. And even if I had access, I'm doubtful that I'd have any commentary to contribute.

In summary, I recommend leaving it alone but if you can't resist

(So it's a temptation to be resisted, rather than an opportunity to be considered.)

consider just changing "Environment Record" to "Binding Record" and then converting "Lexical Environments" into records so you can use dot-notation.

Yup, I think I'll start a PR along those lines.

All 10 comments

cc @allenwb @bterlson for historical context?

(Lexical Environments and Environment Records first appeared in ES5 [more precisely, in the 2008-11-03 draft of ES3.1], looking more-or-less the way they do now. In prior editions, there was a "scope chain" that wasn't really formalized.)

Lexical Environments and Environment Records are used to specify two distinct but related parts of the the ES language semantics. Environment Records are used to specify how the bindings defined in a single scope map identifiers to values with different kinds of Environment Records correspond to the the different kinds of identifier binding scopes provided by the language. Lexical Environments are used to specify the semantics of nested lexical scoping including how the Environment Record that provides the active binding for a specific identifier is selected. This distinction might have been clearer if I had used the term "Bindings Record" in place of "Environment Record".

You could combine them into a single abstraction, but that blurs the important distinction between their semantics and creates other irregularities. For example, every such combined abstraction would have an [[Outer]] field and nesting operations even though some uses of the the abstraction (such as the subparts of a Global Environment Record) never use them.

I'm skeptical of the value of changes such of these unless they are motivated by the need to introduce a new feature into the language. Otherwise they are just churn that carry the risk of unintentional semantic change and require established user of the spec. to relearn how to interpret parts of the specification. They also risk invalidate portions of external commentaries or books that attempt to educate readers about the ES spec. I wonder if such efforts wouldn't be better spent on contributing commentary to the TC39 rationale project.

In summary, I recommend leaving it alone but if you can't resist consider just changing "Environment Record" to "Binding Record" and then converting "Lexical Environments" into records so you can use dot-notation.

The latter seems immediately valuable; the former seems valuable only if the general consensus is that it would reduce confusion.

You could combine them into a single abstraction, but that blurs the important distinction between their semantics

I understand the distinction, but I don't understand why that distinction is thought to be important. E.g., is there somewhere you can point to and say "Here's where the spec benefits from making that distinction"?

and creates other irregularities. For example, every such combined abstraction would have an [[Outer]] field

Yup, and for a global environment it'd be *null*, like now.

and nesting operations

(I guess you mean the NewFooEnvironment() operations, as they're the ones that set up the nesting.)

even though some uses of the the abstraction (such as the subparts of a Global Environment Record) never use them.

Huh? Those things would be used exactly as much after the suggested merge as they are currently. Why is that an irregularity?


I'm skeptical of the value of changes such of these unless they are motivated by the need to introduce a new feature into the language.

Whereas I think the spec has been improved by changes that didn't have that motivation.

Otherwise they are just churn that carry the risk of unintentional semantic change and require established user of the spec. to relearn how to interpret parts of the specification. They also risk invalidate portions of external commentaries or books that attempt to educate readers about the ES spec.

(Note that the commentary you linked to doesn't even mention Lexical Environments or Environment Records. However, I grant that such commentaries and books exist.) Yes, those are risks of change. But it's not all doom and gloom. Refactoring can also:

  • uncover spec bugs.
  • make it easier for people to read + understand the spec.
  • make it easier for commentaries to explain the spec.

The editors have to weigh the pros and cons of each case.

I wonder if such efforts wouldn't be better spent on contributing commentary to the TC39 rationale project.

That link is a 404 for me, so I can't spend my efforts there. And even if I had access, I'm doubtful that I'd have any commentary to contribute.

In summary, I recommend leaving it alone but if you can't resist

(So it's a temptation to be resisted, rather than an opportunity to be considered.)

consider just changing "Environment Record" to "Binding Record" and then converting "Lexical Environments" into records so you can use dot-notation.

Yup, I think I'll start a PR along those lines.

I can sympethize with the desire to refactor lexical environments; I found the distinction a bit difficult to remember at times. I also admit that, philosophically speaking, I tend to align with Allen: to be frank, I think we "spec-heads" have a tendancy to overestimate the value of these refactorings. (How many spec-heads are there in the world anyway?) If it were soley up to me, I'd probably leave well enough alone.

That said, I'd be more than happy to review a PR from @jmdyck, and I'm open to merging if we can generally agree that it improves understandability and maintainability.

from the perspective of an engine maintainer, these are usually folded into a single structure. from the perspective of someone reading the spec, having both is super confusing to keep track of. combine that with the multiple types of lexical environments or multiple types of environment records or whichever it is and it just gets even worse.

It鈥檇 worth noting that the clearer the spec is, the more spec-heads there are likely to be ;-)

Instead, we could make [[Outer]] a field common to all Environment Records, and then replace every Lexical Environment with the Environment Record that it refers to.

I've been working on a JS parser lately, and ive done literally this, the added layer didn't bring much benefit, so i've been adding outer_env to each record as an added field.
example here:
https://github.com/jasonwilliams/boa/blob/environmentrecord/src/lib/environment/declerative_environment_record.rs#L153-L155

I believe that this is now completed via #1697.

Was this page helpful?
0 / 5 - 0 ratings