Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Something about Invariants in specification (needs clarification). #1628

Closed
dSalieri opened this issue Jul 14, 2019 · 54 comments
Closed

Something about Invariants in specification (needs clarification). #1628

dSalieri opened this issue Jul 14, 2019 · 54 comments
Labels

Comments

@dSalieri
Copy link

dSalieri commented Jul 14, 2019

In the specification there is such a term as "invariants". As I understand it, it is used for assertions in algorithms that must be enforced. I noticed that the specification in chapter 6.1.7.3 says to which objects these invariants are applied: to ordinary and standard exotic objects. But exotic proxy objects are also casually mentioned there, the sentence sounds like:

ECMAScript Proxy objects maintain these invariants by means of runtime checks on the result of traps invoked on the [[ProxyHandler]] object.

I honestly do not quite understand what specification wanted to say this proposal. Can you explain this? If you can obviously somehow demonstrate this sentence would be great.

Now let me take the invariants for the [[Get]] internal method:

[[Get]] ( P, Receiver )

  • If P was previously observed as a non-configurable, non-writable own data property of the target with value V, then [[Get]] must return the SameValue as V.
  • If P was previously observed as a non-configurable own accessor property of the target whose [[Get]] attribute is undefined, the [[Get]] operation must return undefined.

The first invariant is interesting here.

  1. The argument V is not found in the parameters of the internal [[Get]] method.
  2. The end of the sentence: ...then [[Get]] must return the SameValue as V. SameValue has 2 parameters, and there are no arguments for the transfer. How to understand this?
@dSalieri dSalieri changed the title Something about Invariants in specification. Something about Invariants in specification (need clarification). Jul 14, 2019
@dSalieri dSalieri changed the title Something about Invariants in specification (need clarification). Something about Invariants in specification (needs clarification). Jul 14, 2019
@bathos
Copy link
Contributor

bathos commented Jul 15, 2019

For all objects except proxy exotic objects (or I guess host exotic objects, not sure what the correct term is for that), the invariants require no ‘enforcement’ because they ‘fall out’ of the algorithms themselves. For proxy exotic objects, where ES code may implement the meat of the object internal methods, the proxy objects’s actual object internal methods are like wrappers around the user code, and those wrappers have to explicitly perform checks to ensure that the should-be invariants are met, since the user code may have attempted violating them.

The argument V is not found in the parameters of the internal [[Get]] method.

V is not an argument to [[Get]]. This variable is being defined in the invariant statement itself (a previous return value of [[Get]] when the stated criteria were met). When it says ‘with value V’ that means ‘with a specific value we will refer to as V’.

@devsnek
Copy link
Member

devsnek commented Jul 15, 2019

just to clarify: if we take proxy's [[Get]] as an example (https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver)

It calls the proxy's get handler, but it doesn't just return the result of that. It then checks that the descriptor for that property matches the invariant on [[Get]], that non-writable non-configurable properties with value V must return that same value V from [[Get]].

If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.

@allenwb
Copy link
Member

allenwb commented Jul 15, 2019

For all objects except proxy exotic objects (or I guess host exotic objects, not sure what the correct term is for that), the invariants require no ‘enforcement’ because they ‘fall out’ of the algorithms themselves.

The "essential invariants of objects" are stated in the spec because they are requirements imposed upon all objects, whether defined by a the specification, by an "engine" as extensions to the specification, by an host environment, or by user code.

The specification takes care of ensuring that the specified built-in objects all conform and that user defined exotic objects implemented via Proxy also conform.

But future spec writers defining new kinds of objects, engine implementors, and host implementors must ensure that any exotic objects they define conform to the invariants. Similarly any new mechanisms that allow user code to define exotic objects must ensure that the user defined objects conform to the invariants, just like Proxy does.

@dSalieri
Copy link
Author

@devsnek Something tells me that you're talking to me a little about something else. There is [[Get]] for ordinary objects, and there is for proxy [[Get]]. In your example, the line [[Get]] from the proxy. And I asked a question about [[Get]] for ordinary objects. Yes, in the proxy [[Get]] has a line that you described, but not in ordinary.
I am interested in this:

If P was previously observed as a non-configurable, non-writable own data property of the target with value V, then [[Get]] must return the SameValue as V.

for ordinary objects.

And there was not a word about this sentence:

ECMAScript Proxy objects maintain these invariants by means of runtime checks on the result of traps invoked on the [[ProxyHandler]] object.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 15, 2019

And I asked a question about [[Get]] for ordinary objects.

In your original post, you only used the word "ordinary" once, when paraphrasing 6.1.7.3.

I am interested in this:

If P was previously observed as a non-configurable, non-writable own data property of the target with value V, then [[Get]] must return the SameValue as V.

for ordinary objects.

Ah, well, you didn't make that clear before.

For an ordinary object, [[Get]] is 'implemented' by invoking OrdinaryGet. There, if _P_ identifies an own data property, step 4 simply returns its value. So for ordinary objects, the invariant you're interested in is maintained, not by any action of the [[Get]] internal method itself, but rather by the actions of other internal methods. For example, an attempt to [[Set]] such a property will (in OrdinarySetWithOwnDescriptor) fail to change the property's value. And if nothing can change the property's value, then the value returned by [[Get]] will be the same as any value it was previously observed to have. (It's actually more complicated than that, but it's a start.)

And there was not a word about this sentence:

ECMAScript Proxy objects maintain these invariants by means of runtime checks on the result of traps invoked on the [[ProxyHandler]] object.

@bathos and @devsnek both addressed this sentence. You should probably re-read their answers and then ask something more specific if necessary.

@bathos
Copy link
Contributor

bathos commented Jul 15, 2019

And there was not a word about this sentence:

ECMAScript Proxy objects maintain these invariants by means of runtime checks on the result of traps invoked on the [[ProxyHandler]] object.

I may have done a poor job of communicating what (I think) this means, so here’s another go:

Certain facets of object behavior (including behavior over time) are epiphenomena that emerge from the sum of smaller details that may span multiple algorithms. They aren’t always obvious. The invariant statements make these behavioral properties explicit and say ‘this effect is intentional, and it is required that it be maintained even by exotic objects with custom internal methods’.

In the example about [[Get]], it’s saying that once a property has been reported as an unwritable and unconfigurable data property once, the value returned by [[Get]] must never change again.

If this weren’t established as an invariant, a property being unconfigurable and unwritable would cease to reliably have any meaning.

Because a Proxy handler function could report any value at any time, the internal methods for Proxy exotic objects include steps that exist just to ensure the invariants don’t get violated. If the result of a proxy handler function (trap) would violate an invariant, these algorithms throw a TypeError instead, thereby maintaining the invariant. That’s what ‘runtime checks’ refers to. Other internal methods don’t need these same checks because they are already defined in such a way that provably will not violate the invariants.

@dSalieri
Copy link
Author

@jmdyck, actually by the context one could understand what I meant.
But oh well.

You say that for ordinary objects [[Get]] is implemented via OrdinaryGet. And step 4 returns the value of the object.
But what do you mean:

So for ordinary objects, the invariant you're interested in is maintained, not by any action of the [[Get]] internal method itself, but rather by the actions of other internal methods.

In general, I would just like to see how an invariant will be executed for a normal and proxy object, with valid js code, this would be the best explanation.

What @bathos and @devsnek answered is implicit to me. Since they do not specify specifically, referring to the proposal I have indicated:

ECMAScript Proxy objects maintain these invariants by means of runtime checks on the result of traps invoked on the [[ProxyHandler]] object.

Is it possible to explicitly indicate what will be: ... result of traps invoked on the [[ProxyHandler]] object? And for this result will be performed the usual invariants?

@bathos
Copy link
Contributor

bathos commented Jul 15, 2019

I’m confused about the final question, but regarding ‘execution’ of invariants — they aren’t executed. The invariants are just true statements about object behavior. Proxy objects have a unique need to perform runtime checks that correspond to these invariants because user code (via the handlers) might not do the right thing. Everything else is known to ‘play by the rules’ in advance; the invariants are satisfied by definition.

For example, imagine a rule that an internal method [[Increment]] must always return an integer, and the integer returned must always either be greater than the previous value that was returned by [[Increment]] or must be the max safe integer. For ordinary objects, [[Increment]] might be defined to add 1 to a stored value and return the result. This algorithm is known to satisfy the invariant. No runtime check needs to be made.

Now imagine a Proxy trap for this internal method. The trap function is user code, and we could define it as () => 2. If the proxy just called the handler and directly returned its result, the invariant would be satisfied the first time this method is called, but not the second time. The ‘laws of physics’ would break down. So instead, the proxy’s [[Increment]] internal method must wrap the call to user code, and perform a check to make sure the new value (result of trap) is larger than any previously reported value.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 15, 2019

But what do you mean:

So for ordinary objects, the invariant you're interested in is maintained, not by any action of the [[Get]] internal method itself, but rather by the actions of other internal methods.

I gave an example of what I meant, in the text following the sentence you just quoted. If you want clarification, you'll have to say what part is unclear.

In general, I would just like to see how an invariant will be executed for a normal and proxy object,

They aren't executed, they are either satisfied or not. If you like, you can imagine an auditor examining the result of every invocation of every internal method of every object. If those results ever fail to satisfy the invariants, then it's not a conforming implementation.

ECMAScript Proxy objects maintain these invariants by means of runtime checks on the result of traps invoked on the [[ProxyHandler]] object.

Is it possible to explicitly indicate what will be: ... result of traps invoked on the [[ProxyHandler]] object?

In @devsnek's example, the "trap" is the handler object's "get" method, which is obtained and bound to the trap alias in step 6. This is then invoked on the handler object in step 8, with the result being bound to trapResult.

And for this result will be performed the usual invariants?

No, you don't "perform" invariants. Rather, step 10 performs runtime checks on trapResult in order to ensure that the invariants are satisfied.

@dSalieri
Copy link
Author

@bathos sorry, I meant enforce instead perform. That is, for ordinary objects, invariants show their standard behavior in algorithms (but what's the point?) As for the proxy, it is clear that the user can create his own functions to override the behavior of internal methods.

You gave an example with the abstract [[Increment]]. An invariant of which is such a statement for ordinary objects: internal method [[Increment]] must always return an integer, and the integer returned must always either be greater than the previous value that was returned by [[Increment]] or must be the max safe integer. What did you mean by max safe integer? (I know this is a constant in js)

Next you talk about the proxy. As I understand the invariant remains the same as in the case of ordinary objects. The first time is satisfied because max safe integer? (It is still not clear what is meant by this). The second time is not satisfied because the number is not changed?

If we are talking about the difference of algorithms for ordinary and proxy objects, then I understand.
It is clear that we cannot use the algorithm from ordinary objects for proxies, it simply will not work. But if we are talking about a specific stage or steps of the algorithm for a proxy is another matter.
If I understand you correctly:
So instead, the proxy’s [[Increment]] internal method must wrap the call to user code, and perform a check to make sure the new value (result of trap) is larger than any previously reported value.
Do you want to say that the custom code is wrapped by an internal proxy method for checking using invariants of ordinary objects?

@bathos
Copy link
Contributor

bathos commented Jul 15, 2019

What did you mean by max safe integer?

Sorry, qualifying the toy example w/ ‘or max safe integer’ probably just added unhelpful noise; I was just hedging against a potential point of confusion about what I intended to communicate. It is probably better if that part is ignored.

The second time is not satisfied because the number is not changed?

Yes. The first time it’s okay (in this imaginary example) because there’s no prior number result, the second time it is not okay because the new number is not larger than the previous number.

Do you want to say that the custom code is wrapped by an internal proxy method for checking using invariants of ordinary objects?

Yes — I think so, anyway. There may be a terminology disconnect here, but it sounds like you have the gist. The proxy object’s internal methods don’t ‘trust’ the result of the trap, they have to take extra care to make sure the result conforms to the rules that other objects exhibit.

@dSalieri
Copy link
Author

dSalieri commented Jul 16, 2019

@bathos That is, it turns out a double check for matching invariants when it comes to a proxy object. The first check is the check of the so-called “trap”, which is checked by invariants for ordinary objects, the second check is the check of the object proxy itself on its proxy invariants.


@jmdyck
Sorry, not execute, I meant enforce. But your explanation of my error is exellent.

Ok, then I am interested in the following:

  1. When the invariant will work (ordinary):

    If P was previously observed as a non-configurable, non-writable own data property of the target with value V, then [[Get]] must return the SameValue as V.

    After all, it is defined in the invariants section of ordinary and exotic objects, but not for proxies. And you said that this does not work when calling this internal method directly. That is, even the abstract GetValue operation is not suitable here? Then somehow it all looks strange when the method has some invariants, does not enforce them in the method for which they are intended. I hope you understand my confusion.

  2. When the invariant will work (proxy):

    The value reported for a property must be the same as the value of the corresponding target object property if the target object property is a non-writable, non-configurable own data property.

    It is desirable on real examples.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 16, 2019

That is, it turns out a double check for matching invariants when it comes to a proxy object. The first check is the check of the so-called “trap”, which is checked by invariants for ordinary objects, the second check is the check of the object proxy itself on its proxy invariants.

There aren't "invariants for ordinary objects" and "proxy invariants". There are only invariants for all objects.

Ok, then I am interested in the following:

  1. When the invariant will work (ordinary):

    If P was previously observed as a non-configurable, non-writable own data property of the target with value V, then [[Get]] must return the SameValue as V.

After all, it is defined in the invariants section of ordinary and exotic objects, but not for proxies.

Yes, for proxies. Proxy objects are exotic objects. Once you've said "ordinary and exotic objects", that's all objects. The invariants specified in 6.1.7.3 Invariants of the Essential Internal Methods are invariants that all objects must satisfy. You should maybe re-read @allenwb's response above.

And you said that this does not work when calling this internal method directly. That is, even the abstract GetValue operation is not suitable here?

I don't understand what you're asking there.

Then somehow it all looks strange when the method has some invariants, does not enforce them in the method for which they are intended. I hope you understand my bewilderment.

I think so. You're puzzled that OrdinaryGet doesn't do anything to ensure that the [[Get]] invariant quoted above is satisfied. I explained this before: the other ordinary internal methods (e.g.., OrdinarySetWithOwnDescriptor for [[Set]]) are arranging things so that OrdinaryGet doesn't have to.

Here's a silly analogy. Imagine a bucket with two "internal methods", [[Put]] which puts something in the bucket, and [[Take]] which returns something from the bucket. And imagine there's an invariant on [[Take]] that says the thing returned must be red. One way to accomplish this is for the [[Take]] method to go through the things in the bucket until it finds a red one, and return that. But a different way is for the [[Put]] method to discard anything that isn't red, so the only things that go in bucket are red. Then [[Take]] just needs to reach in "without looking" and return anything in the bucket, safe in the knowledge that it will be red. So, with the second approach, even though the invariant is about the thing returned by the [[Take]] method, the [[Take]] method doesn't do anything to ensure that it's satisfied.

In general, all of an object's internal methods work together to ensure that all of the invariants are satisfied.

@dSalieri
Copy link
Author

@jmdyck I just made a split, especially with other rules for proxy objects.
For example, for [[GetPrototypeOf]]:

  1. Ordinaries and exotics:

[[GetPrototypeOf]] ( )

  • The Type of the return value must be either Object or Null.
  • If target is non-extensible, and [[GetPrototypeOf]] returns a value V, then any future calls to [[GetPrototypeOf]] should return the SameValue as V.
  1. Proxies:

[[GetPrototypeOf]] for proxy objects enforces the following invariants:

  • The result of [[GetPrototypeOf]] must be either an Object or null.
  • If the target object is not extensible, [[GetPrototypeOf]] applied to the proxy object must return the same value as [[GetPrototypeOf]] applied to the proxy object's target object.

If we compare the first line in 1 and 2, then we note that this is the same thing. Or not? If not the same thing, then it is necessary to make a more obvious difference of what is meant.

I read the answer @allenwb, it says that "essential invariants of objects" apply to all objects no matter how and where they are created. Although it may be wrong, let alone proxy objects have their own invariants. Although perhaps implicitly, he meant all objects with the exception of a proxy.

I don't understand what you're asking there.

I meant this(your sentence): So for ordinary objects, the invariant you're interested in is maintained, not by any action of the [[Get]] internal method itself, but rather by the actions of other internal methods.

So, with the second approach, even though the invariant is about the thing returned by the [[Take]] method, the [[Take]] method doesn't do anything to ensure that it's satisfied.

And what's the point? If exists an invariant for a method that is not enforced in it?
And for what this part of the invariant: ... then [[Get]] must return the SameValue as V - if it is not appears anywhere in [[Set]]?

P.S I understood your explanation: an invariant for a particular object does not have to be enforced in the method itself for which it is declared.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 16, 2019

For example, for [[GetPrototypeOf]]:
[...]
If we compare the first line in 1 and 2, then we note that this is the same thing.

Right. The spec is telling you that the [[GetPrototypeOf]] internal method for proxies enforces the [[GetPrototypeOf]] invariants that are specified for all objects.

I read the answer @allenwb, it says that "essential invariants of objects" apply to all objects no matter how and where they are created. Although it may be wrong, let alone proxy objects have their own invariants.

Proxy objects are free to maintain additional or more specific invariants, but they are definitely still obliged to maintain the invariants specified in 6.1.7.3.

Although perhaps implicitly, he meant all objects with the exception of a proxy.

No, he did not.

And for what this part of the invariant: ... then [[Get]] must return the SameValue as V - if it is not appears anywhere in [[Set]]?

An invariant isn't something that has to "appear" in any internal method. It's a condition that an object's internal methods must (somehow) satisfy. How they do so is not necessarily going to be obvious.

For the case of ordinary objects, the [[Set]] internal method does indeed help to enforce the [[Get]] invariant. I said how in my first response.

P.S I understood your explanation: an invariant for a particular object does not have to be enforced in the method itself for which it is declared.

Great! But if you understand that, then I don't get why you asked:

And what's the point? If exists an invariant for a method that is not enforced in it?

@dSalieri
Copy link
Author

@jmdyck

Right. The spec is telling you that the [[GetPrototypeOf]] internal method for proxies enforces the [[GetPrototypeOf]] invariants that are specified for all objects.

That is, invariants for proxy objects are summed up? The result of [[GetPrototypeOf]] is a step from the proxy algorithm (13) or from the proxy steps of the algorithm (6 and 11) where are [[GetPrototypeOf]] from normal objects used?

An invariant isn't something that has to "appear" in any internal method. It's a condition that an object's internal methods must (somehow) satisfy. How they do so is not necessarily going to be obvious.

If I understand you correctly, then any invariant does not have to be enforced in the method for which it is intended, besides, it does not have to be enforced somewhere else, but it will be enforced where the place is most appropriate to enforced this invariant.

For the case of ordinary objects, the [[Set]] internal method does indeed help to enforce the [[Get]] invariant. I said how in my first response.

Otherwise, I don’t quite understand that part: ... then [[Get]] must return the SameValue as V, if SameValue is not encountered to enforced the invariant (I read your first answer, but it doesn’t say anything about SameValue’s absence [[Set]]). That is, what is the point in such an invariant that uses an explicit indication of what needs to be done, but does not correspond to it. Do you understand?

But if you understand that, then I don't get why you asked:

This refers to SameValue (what I wrote in the paragraph above, in this message)

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 17, 2019

That is, invariants for proxy objects are summed up? The result of [[GetPrototypeOf]] is a step from the proxy algorithm (13) or from the proxy steps of the algorithm (6 and 11) where are [[GetPrototypeOf]] from normal objects used?

In the [[GetPrototypeOf]] internal method for proxies, the calls to target.[[GetPrototypeOf]]() at steps 6.a and 11 might invoke [[GetPrototypeOf]] for ordinary objects, or might not. It depends on whether or not target is an ordinary object.

Other than that, I'm not sure what you were asking.

An invariant isn't something that has to "appear" in any internal method. It's a condition that an object's internal methods must (somehow) satisfy. How they do so is not necessarily going to be obvious.

If I understand you correctly, then any invariant does not have to be enforced in the method for which it is intended, besides, it does not have to be enforced somewhere else, but it will be enforced where the place is most appropriate to enforced this invariant.

That's probably an okay understanding of the situation.

For the case of ordinary objects, the [[Set]] internal method does indeed help to enforce the [[Get]] invariant. I said how in my first response.

Otherwise, I don’t quite understand that part: ... then [[Get]] must return the SameValue as V, if SameValue is not encountered to enforced the invariant (I read your first answer, but it doesn’t say anything about SameValue’s absence [[Set]]). That is, what is the point in such an invariant that uses an explicit indication of what needs to be done, but does not correspond to it. Do you understand?

I think so, You're saying that if the invariant refers to a specific operation, then you'd expect to see that operation also referenced by the code that enforces that invariant. But no, that expectation isn't warranted, especially when the operation in question in SameValue.

Roughly speaking, the first [[Get]] invariant is saying that if property P is a non-configurable, non-writable own data property, then each call to [[Get]] must return the same value (according to the definition of SameValue). This is more-or-less equivalent to saying that any attempt to change the value of P must not succeed -- as long as nothing can change the value of P, then the value returned by [[Get]] will always be the SameValue. But you don't accomplish that by checking whether P's current value is the SameValue as some previous value, you accomplish that by preventing changes to P's value.

@dSalieri
Copy link
Author

dSalieri commented Jul 17, 2019

@jmdyck

Other than that, I'm not sure what you were asking.

This question was related to

The result of [[GetPrototypeOf]] must be either an Object or null.

for proxy algorithm [[GetPrototypeOf]]. What is result of [[GetPrototypeOf]]: steps (6, 11) or (13)?

I think so, You're saying that if the invariant refers to a specific operation, then you'd expect to see that operation also referenced by the code that enforces that invariant. But no, that expectation isn't warranted, especially when the operation in question in SameValue.

I just do not understand the logic of the specification: why specify a specific operation, and then not enforce it?

All references to SameValue are according to the definition of the SameValue algorithm.

Why it was impossible to write it easier? Why is it needed? Maybe she means not the meaning that I put into it?

Roughly speaking, the first [[Get]] invariant is saying that if property P is a non-configurable, non-writable own data property, then each call to [[Get]] must return the same value (according to the definition of SameValue). This is more-or-less equivalent to saying that any attempt to change the value of P must not succeed -- as long as nothing can change the value of P, then the value returned by [[Get]] will always be the SameValue. But you don't accomplish that by checking whether P's current value is the SameValue as some previous value, you accomplish that by preventing changes to P's value.

You said: according to the definition of SameValue - what is meant by this?
And why I don't accomplish that by checking whether P's current value is the SameValue as some previous value?

@bathos
Copy link
Contributor

bathos commented Jul 18, 2019

And why I don't accomplish that by checking whether P's current value is the SameValue as some previous value?

Because in this case, a runtime check would be pointless: it is already provable that the result satisfies the invariant. Runtime checks corresponding to invariant rules are a special and rare case. They are only needed when some part of the algorithm depends on the result of invoking arbitrary user code.

So why specify it for all objects? For one, it’s broadly useful to be able to know what reliable facts exist about them. But more importantly, implementations may expose their own exotic objects with their own definitions for their internal object methods. The invariants tell us facts about objects, and it is these facts that will be important for people implementing new exotic objects to understand, because if these facts aren’t true for their object, it’s not a JS object.

It’s saying ‘a JS object behaves as follows’. It’s not an operation or an algorithm, it’s a ‘truth’, like in formal logic.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 18, 2019

This question was related to

The result of [[GetPrototypeOf]] must be either an Object or null.

for proxy algorithm [[GetPrototypeOf]]. What is result of [[GetPrototypeOf]]: steps (6, 11) or (13)?

The invariant applies to the result of the [[GetPrototypeOf]] internal method for all objects. So for a proxy object, it applies to the result returned at 6.a, 10, or 13. And for the proxy object's target object, it applies to the result of the invocations at 6.a and 11 (which are, of course, the value returned by some (possibly other) [[GetPrototypeOf]] internal method).

I think so, You're saying that if the invariant refers to a specific operation, then you'd expect to see that operation also referenced by the code that enforces that invariant. But no, that expectation isn't warranted, especially when the operation in question in SameValue.

I just do not understand the logic of the specification: why specify a specific operation, and then not enforce it?

It is enforced! But you can enforce it without referencing it. I don't know how to make that plainer.

All references to SameValue are according to the definition of the SameValue algorithm.

Why it was impossible to write it easier? Why is it needed? Maybe she means not the meaning that I put into it?

An invocation normally looks like SameValue(X, Y) but the invariants don't use that syntax, so the sentence might be there to reassure the reader that the spec really does mean that operation.

You said: according to the definition of SameValue - what is meant by this?

It means "according to the definition of the SameValue operation in 7.2.10". Or, more long-windedly, "X is the SameValue as Y" means "SameValue(X, Y). as defined in 7.2.10, would return true".

And why I don't accomplish that by checking whether P's current value is the SameValue as some previous value?

Say you did. If SameValue told you they were different, you'd have to return the previous value (to maintain the invariant). And if they were the same, you could return either, so might as well return the previous. In which case, you'd have no need of the "current" value as a separate value. Just keep the previous value as the current value. I.e. don't allow the value of the property to change.

@dSalieri
Copy link
Author

@jmdyck

The invariant applies to the result of the [[GetPrototypeOf]] internal method for all objects.

Wait for this invariant for proxies, why do you say that it is applied to the result of the [[GetPrototypeOf]] internal method for all objects? And why do you push the target object of the proxy object and the proxy object itself in step 6.a? This is due to the fact that maybe instead of the ordinary object of the proxy object? And therefore you need to apply the invariant for the proxy object as a target?

I just do not understand the logic of the specification: why specify a specific operation, and then not enforce it?

It is enforced! But you can enforce it without referencing it. I don't know how to make that plainer.

I meant that the specification does not use the SameValue operation in its common and normal form with arguments, but uses it as an abstract term. In my opinion, this complicates the understanding of the chapter with invariants. This thing needs to be more clearly explained. Or still abandon the term SameValue.

An invocation normally looks like SameValue(X, Y) but the invariants don't use that syntax, so the sentence might be there to reassure the reader that the spec really does mean that operation.

If the specification tries to assure the reader that this operation is used, then it is worth making it more obvious, because the reader may be at a loss that he will not find this operation in the algorithms.

Well let's say the specification tells us that this operation is SameValue. But I suppose I can’t understand how it is used if the usual programmer’s call is not used through the name, parentheses and argument passing. What should I imagine to understand?

It means "according to the definition of the SameValue operation in 7.2.10". Or, more long-windedly, "X is the SameValue as Y" means "SameValue(X, Y). as defined in 7.2.10, would return true".

Well, although you gave a free definition of a SameValue operation. The complete definition of the operation I think is the whole algorithm (including production steps), which provides the name SameValue. Well, that is, again, the specification if I am not mistaken, does not say what the definition of SameValue is. She should say it more clearly, to understand unambiguously what is meant by: according to the definition of SameValue.

But you don't accomplish that by checking whether P's current value is the SameValue as some previous value, you accomplish that by preventing changes to P's value.

This is your sentence. I already asked about him. But I just do not understand what it is about, even in the context of your paragraph where it is present.

Why am I not accomplishing? Do not accomplish what? Invariant?! I do not understand the logical chain in which your sentence stands, specifically I do not understand what the accomplish refers to.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 18, 2019

The invariant applies to the result of the [[GetPrototypeOf]] internal method for all objects.

Wait for this invariant for proxies, why do you say that it is applied to the result of the [[GetPrototypeOf]] internal method for all objects?

Because it's not an invariant for just proxies, it's an invariant for all objects. We went over this two days ago.

And why do you push the target object of the proxy object and the proxy object itself in step 6.a?

I don't know what you mean by "push" there.

This is due to the fact that maybe instead of the ordinary object of the proxy object?

If I understand you correctly: Yes, the proxy's target object could be (instead of an ordinary object) another proxy object. Or any other exotic object.

And therefore you need to apply the invariant for the proxy object as a target?

It doesn't really matter what kind of object the proxy's target object is: it must enforce all the invariants in 6.1.7.3. So the calls to target.[[GetPrototypeOf]]() at 6.a and 11 must satisfy the [[GetPrototypeOf]] invariants.

I meant that the specification does not use the SameValue operation in its common and normal form with arguments, but uses it as an abstract term. In my opinion, this complicates the understanding of the chapter with invariants.

I'm pretty sure I disagree. But you're free to suggest changes that you think would improve the spec.

An invocation normally looks like SameValue(X, Y) but the invariants don't use that syntax, so the sentence might be there to reassure the reader that the spec really does mean that operation.

If the specification tries to assure the reader that this operation is used, then it is worth making it more obvious, because the reader may be at a loss that he will not find this operation in the algorithms.

My guess is that such a reader would be at a loss regardless of how obvious we make the connection between Samevalue in the invariants and SameValue the operation defined in 7.2.10.

Well let's say the specification tells us that this operation is SameValue. But I suppose I can't understand how it is used if the usual programmer's call is not used through the name, parentheses and argument passing. What should I imagine to understand?

If you can't figure that out yourself, then I think maybe ECMA-262 isn't for you. As currently written, it doesn't "hold your hand" or make everything as obvious as you want. It might improve in this area, but probably not much and probably not soon.

But if you don't want to give up on the spec, you could just skip 6.1.7.3. I'd say you only need to understand it if you're adding non-standard exotics to an implementation (or maybe if you're building an implementation for which adding non-standard exotics is a future possibility).

But you don't accomplish that by checking whether P's current value is the SameValue as some previous value, you accomplish that by preventing changes to P's value.

This is your sentence. I already asked about him. But I just do not understand what it is about, even in the context of your paragraph where it is present.

Why am I not accomplishing? Do not accomplish what? Invariant?! I do not understand the logical chain in which your sentence stands, specifically I do not understand what the accomplish refers to.

In the original context of that sentence, you can read "accomplish that" as "enforce the first [[Get]] invariant". Here's the logical chain:

  • Let "NCNWODP" = "non-configurable, non-writable own data property".
  • Roughly speaking, the first [[Get]] invariant is saying that if property P is an NCNWODP, then each call to [[Get]] must return the same value.
  • So how do we arrange that (if P is an NCNWODP) [[Get]] always return the same value?
  • The easiest way is to ensure that P's value never changes. If it never changes, then [[Get]] can just return P's value, and it will always be the same.
  • So how do we ensure that, if P is an NCNWODP, its value never changes?
  • The obvious way is this: at any time that an invocation of an internal method attempts to change P's value, if P is an NCNWODP, do not let the attempt succeed.

@dSalieri
Copy link
Author

dSalieri commented Jul 19, 2019

@jmdyck

Because it's not an invariant for just proxies, it's an invariant for all objects. We went over this two days ago.

Wait for the invariant for all to look like this:

  • The Type of the return value must be either Object or Null.

The proxy invariant looks like this:

  • The result of [[GetPrototypeOf]] must be either an Object or null.

The invariant given by me was taken from a proxy. Why do you then say that it is for all, and not just for a proxy? I misunderstood something?

I don't know what you mean by "push" there.

I meant intersectability, that is, how a proxy object can be in 6.a as well as a ordinary object.

If I understand you correctly: Yes, the proxy's target object could be (instead of an ordinary object) another proxy object. Or any other exotic object.

Yes, that's it.

It doesn't really matter what kind of object the proxy's target object is: it must enforce all the invariants in 6.1.7.3. So the calls to target.[[GetPrototypeOf]]() at 6.a and 11 must satisfy the [[GetPrototypeOf]] invariants.

Yeah, I remember the mention of @allenwb that, regardless of the type of object, the invariants from 6.1.7.3 should be used. This means that proxy objects must follow invariants:

[[GetPrototypeOf]] ( )

  • The Type of the return value must be either Object or Null.
  • If target is non-extensible, and [[GetPrototypeOf]] returns a value V, then any future calls to [[GetPrototypeOf]] should return the SameValue as V.

[[GetPrototypeOf]] for proxy objects enforces the following invariants:

  • The result of [[GetPrototypeOf]] must be either an Object or null.
  • If the target object is not extensible, [[GetPrototypeOf]] applied to the proxy object must return the same value as [[GetPrototypeOf]] applied to the proxy object's target object.

But then there is a slight misunderstanding. In fact, the lower two invariants overlap the two upper invariants, because they are the same (but this only works for proxies). And as mentioned earlier, the invariants from 6.1.7.3 are applied to all objects. In this case, how does it work? Do you understand what I mean?


I meant that the specification does not use the SameValue operation in its common and normal form with arguments, but uses it as an abstract term. In my opinion, this complicates the understanding of the chapter with invariants.

I'm pretty sure I disagree. But you're free to suggest changes that you think would improve the spec.

I would also agree with you having full knowledge of the situation, everything depends purely on understanding.

My guess is that such a reader would be at a loss regardless of how obvious we make the connection between Samevalue in the invariants and SameValue the operation defined in 7.2.10.

I agree in general, the specification is not easy to read, again if you do not know what is happening in it.

If you can't figure that out yourself, then I think maybe ECMA-262 isn't for you. As currently written, it doesn't "hold your hand" or make everything as obvious as you want. It might improve in this area, but probably not much and probably not soon.

But if you don't want to give up on the spec, you could just skip 6.1.7.3. I'd say you only need to understand it if you're adding non-standard exotics to an implementation (or maybe if you're building an implementation for which adding non-standard exotics is a future possibility).

I do not plan to give up so easily :)

In the original context of that sentence, you can read "accomplish that" as "enforce the first [[Get]] invariant". Here's the logical chain:
...

Great, well explained here.


I have no choice. I have to ask about SameValue again. That is, this operation is not meant in invariants as a standard function that should be called. But as an operation which is implied with meaning. Because we can see an invariant with this operation, but not see this operation where the invariant should be enforced. Right?

To comply with the invariant where SameValue is used, do we need to follow the steps of the SameValue algorithm (Question to according to the definition of SameValue operation)? If this does not mean the execution of all algorithm, then the SameValue operation implies a brief meaning of this operation: matching type and value (within the chapter with invariants).

P.S I don’t like that I have to go back to some questions in a few days and then walk in a circle. I would be happy to ask all the necessary questions on the first day if I knew the situation better.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 19, 2019

Wait for the invariant for all to look like this:

  • The Type of the return value must be either Object or Null.

The proxy invariant looks like this:

  • The result of [[GetPrototypeOf]] must be either an Object or null.

The invariant given by me was taken from a proxy. Why do you then say that it is for all, and not just for a proxy? I misunderstood something?

The Note in 9.5.1 [[GetPrototypeOf]] doesn't define invariants. (You can't define a normative invariant in a Note.) Rather, it's referring to the invariant defined in 6.1.7.3. As I said above:

The spec is telling you that the [[GetPrototypeOf]] internal method for proxies enforces the [[GetPrototypeOf]] invariants that are specified for all objects.


I have no choice. I have to ask about SameValue again. That is, this operation is not meant in invariants as a standard function that should be called. But as an operation which is implied with meaning. Because we can see an invariant with this operation, but not see this operation where the invariant should be enforced. Right?

I'm not sure I'd agree with all of that, but it's probably close enough.

To comply with the invariant where SameValue is used, do we need to follow the steps of the SameValue algorithm

No. (Lots of explanation to that effect above.)

(Question to according to the definition of SameValue operation)?

The invariant is saying that if (hypothetically) you were to compare these two values using SameValue (according to the definition in 7.2.10), then it would return true. But it's definitely not requiring the object to perform that comparison.

Going back to the idea of an imaginary auditor checking that every object satisfies the invariants, you can certainly imagine that the auditor would invoke the SameValue operation.


P.S I don’t like that I have to go back to some questions in a few days and then walk in a circle. I would be happy to ask all the necessary questions on the first day if I knew the situation better.

Not asking all the questions on the first day isn't the problem; rather the problem is asking questions that have already been answered.

@dSalieri
Copy link
Author

dSalieri commented Jul 21, 2019

@jmdyck

The Note in 9.5.1 [[GetPrototypeOf]] doesn't define invariants. (You can't define a normative invariant in a Note.) Rather, it's referring to the invariant defined in 6.1.7.3.

That is, this note in 9.5.1 shows a feature of invariants for proxies? But in fact these are the same invariants from 6.1.7.3?


That is, for the invariant the sense of the SameValue operation is important - return true if the values ​​match?
Perhaps I expressed a little unintelligible my thought earlier, but once again in a slightly different manner:

What is the SameValue definition? This (excluding algorithm steps)?

The internal comparison abstract operation SameValue(x, y), where x and y are ECMAScript language values, produces true or false. Such a comparison is performed as follows:

If this is a definition for SameValue, then read the following invariant:

If P was previously observed as a non-configurable, non-writable own data property of the target with value V, then [[Get]] must return the SameValue as V.

There are problems with understanding. If we read this sentence and read the operation as a term SameValue, and not as the phrase "same value" (which gives us a sense rather than a name like "XYZOperation"), then based on the definition that I gave above, the returned value by the operation SameValue maybe as true so as false. And all because this invariant does not specify at least the value that must be returned by the operation so that it becomes clear that we need a result that returns true, which corresponds to identical values. Oof, I hope I clearly stated the think.

The problem is not that I ask questions for which there is an answer, but that I don’t understand them enough. Similar questions come out of it. Believe me before I ask the questions again, I carefully reads the previous answers.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 21, 2019

@jmdyck

The Note in 9.5.1 [[GetPrototypeOf]] doesn't define invariants. (You can't define a normative invariant in a Note.) Rather, it's referring to the invariant defined in 6.1.7.3.

That is, this note in 9.5.1 shows a feature of invariants for proxies? But in fact these are the same invariants from 6.1.7.3?

Hm. I'm not sure what you mean by "a feature of invariants". And the phrase "invariants for proxies" makes me think you still haven't quite understood. But yes, the invariants in the Note in 9.5.1 directly correspond to the [[GetPrototypeOf]] invariants in 6.1.7.3.

Like I said before: this Note is telling you that the [[GetPrototypeOf]] internal method for proxies enforces the [[GetPrototypeOf]] invariants that are specified (in 6.1.7.3) for all objects.


That is, for the invariant the sense of the SameValue operation is important - return true if the values ​​match?

Yes.

What is the SameValue definition? This (excluding algorithm steps)?

You can't exclude the algorithm steps. (What do you think you achieve by excluding the algorithm steps?)

The internal comparison abstract operation SameValue(x, y), where x and y are ECMAScript language values, produces true or false. Such a comparison is performed as follows:

That's what we call the operation's preamble. It (hopefully) tells you things that are true of the operation, but it certainly isn't the definition of the operation. In fact, it's often completely superfluous, and many operations don't have one.

If this is a definition for SameValue, then read the following invariant:

If P was previously observed as a non-configurable, non-writable own data property of the target with value V, then [[Get]] must return the SameValue as V.

There are problems with understanding. If we read this sentence and read the operation as a term SameValue, and not as the phrase "same value" (which gives us a sense rather than a name like "XYZOperation"), then based on the definition that I gave above, the returned value by the operation SameValue maybe as true so as false. And all because this invariant does not specify at least the value that must be returned by the operation so that it becomes clear that we need a result that returns true, which corresponds to identical values. Oof, I hope I clearly stated the think.

Clear enough, I think, and a fair point, but I've already addressed it:

"X is the SameValue as Y" means "SameValue(X, Y). as defined in 7.2.10, would return true"

@dSalieri
Copy link
Author

@jmdyck
As you said:

You can't define a normative invariant in a Note.

then in 9.5.1 it is specified how the invariant for the proxy object from 6.1.7.3 should be enforced? (Yes, so far this moment is not clear to me using invariants in a note)


You can't exclude the algorithm steps. (What do you think you achieve by excluding the algorithm steps?)

I think then I lose the whole point of the operation. Well, it turns out the introductory text of the operation and the operation steps themselves are the definition?

Clear enough, I think, and a fair point, but I've already addressed it:

"X is the SameValue as Y" means "SameValue(X, Y). as defined in 7.2.10, would return true"

Yes, but the specification is not so written. Well, if logically imagine the meaning of the operation, then I no longer need an explanation for this point.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 21, 2019

then in 9.5.1 it is specified how the invariant for the proxy object from 6.1.7.3 should be enforced?

It would be more accurate to rearrange that slightly: in 9.5.1 it is specified how (two of) the invariants from 6.1.7.3 are enforced for the proxy object.

You can't exclude the algorithm steps. (What do you think you achieve by excluding the algorithm steps?)

I think then I lose the whole point of the operation.

Right.

Well, it turns out the introductory text of the operation and the operation steps themselves are the definition?

And the clause heading, and any other normative text about the operation. (But the introductory text often says little that you couldn't figure out from the other pieces of the definition.)

Clear enough, I think, and a fair point, but I've already addressed it:

"X is the SameValue as Y" means "SameValue(X, Y). as defined in 7.2.10, would return true"

Yes, but the specification is not so written.

If you think it should be, you can request that. I think it's reasonable to expect that readers would understand that just by reading the current text, but if we can make it more obvious at little cost, it might happen.

@dSalieri
Copy link
Author

dSalieri commented Jul 21, 2019

@jmdyck

It would be more accurate to rearrange that slightly: in 9.5.1 it is specified how (two of) the invariants from 6.1.7.3 are enforced for the proxy object.

Do these invariants (9.5.1) override the invariants from 6.1.7.3 for proxies? (I know what you wrote: You can't define a normative invariant in a Note.). Means what for proxy objects the enforcing of invariants changes from 6.1.7.3 to 9.5.1. If I'm wrong again, correct it.

If you think it should be, you can request that. I think it's reasonable to expect that readers would understand that just by reading the current text, but if we can make it more obvious at little cost, it might happen.

Honestly, I do not know how, I just read and ask questions sometimes. I think there are more literate people, instead of me

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 21, 2019

Do these invariants (9.5.1) override the invariants from 6.1.7.3 for proxies?

No.

(I know what you wrote: You can't define a normative invariant in a Note.).

Okay, so do some deduction:

  • You can't define a normative invariant in a Note.
  • Therefore, the invariants that appear in this Note can't be normative.
  • In contrast, the invariants of 6.1.7.3 are normative.
  • So you're asking if a non-normative statement overrides a normative statement.

If that seems like a reasonable question, then I don't think you understand what "normative" and "non-normative" mean. Not that that's a crime, but you maybe should have looked it up by now.

So, again, no. The invariants noted in 9.5.1 do not override the ones stated in 6.1.7.3. They couldn't possibly. Instead, the [[GetPrototypeOf]] internal method for proxy objects, because it enforces the invariants noted in 9.5.1, thereby enforces the corresponding invariants of 6.1.7.3.

Means what for proxy objects the enforcing of invariants changes from 6.1.7.3 to 9.5.1. If I'm wrong again, correct it.

6.1.7.3 doesn't enforce invariants, it states them (defines them, specifies them). So it doesn't make sense to talk about "the enforcing of invariants" changing from 6.1.7.3 to 9.5.1.

@dSalieri
Copy link
Author

@jmdyck now I will try to make a maximum breakthrough to eliminate all questions and get a clean answer (there will be several references to your answers above).

Instead, the [[GetPrototypeOf]] internal method for proxy objects, because it enforces the invariants noted in 9.5.1, thereby enforces the corresponding invariants of 6.1.7.3.

I do not really understand this. You can visually show the dependency as an internal proxy method of the [[GetPrototypeOf]] object using invariants from 9.5.1 ensures the use of invariants from 6.1.7.3. (Maybe I misunderstood you, read my answer completely before answering this question right away)

And seems this quote:

Like I said before: this Note is telling you that the [[GetPrototypeOf]] internal method for proxies enforces the [[GetPrototypeOf]] invariants that are specified (in 6.1.7.3) for all objects.

refers to the quote above. Right?

Next, your sentence:

But yes, the invariants in the Note in 9.5.1 directly correspond to the [[GetPrototypeOf]] invariants in 6.1.7.3.

What do you mean by directly corresponding? (do not rush to answer, read on)

Proxy objects are free to maintain additional or more specific invariants, but they are definitely still obliged to maintain the invariants specified in 6.1.7.3.

If we are talking about the internal proxy method of the object [[GetPrototypeOf]] - are the invariants in the note from 9.5.1 additional invariants or specifying for 6.1.7.3? If these invariants are specifying (9.5.1), then it turns out that they are also invariants that maintain 6.1.7.3? Right? (This question looks like it answers your quotes above.)

Besides I understand what:

You can't define a normative invariant in a Note.

From this it follows that we cannot override normatives by non-normatives. Since the notes are non-normatives.Normatives are mandatory rules to follow. Non-normatives are optional rules, tips, recommendations, may not be follow.

If I understood everything correctly, then taking any proxy object we are obliged to follow the rules of
6.1.7.3, but not required to follow note 9.5.1 (since the note is a non-normative). I correctly understand that 9.5.1 (note) is the same as 6.1.7.3, but 9.5.1 (note) is a specify for 6.1.7.3 of the internal proxy method [[GetPrototypeOf]]?

@bathos
Copy link
Contributor

bathos commented Jul 25, 2019

If I understood everything correctly, then taking any proxy object we are obliged to follow the rules of
6.1.7.3, but not required to follow note 9.5.1 (since the note is a non-normative).

The note in 9.5.1 is not a rule — it cannot be followed or not followed — it is just descriptive. It’s explaining what the algorithm is achieving. The purpose of these notes is to highlight the relationship between steps in the (normative) proxy exotic object internal method algorithms and the (normative) object invariants. Knowing where the invariants are addressed for proxies is helpful for implementors, but even if the notes were removed, the spec would still be saying the same thing.

@dSalieri
Copy link
Author

@bathos If I understand you correctly, the note in 9.5.1 explains how the invariant from 6.1.7.3 enforced?

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 25, 2019

Instead, the [[GetPrototypeOf]] internal method for proxy objects, because it enforces the invariants noted in 9.5.1, thereby enforces the corresponding invariants of 6.1.7.3.

I do not really understand this. You can visually show the dependency as an internal proxy method of the [[GetPrototypeOf]] object using invariants from 9.5.1 ensures the use of invariants from 6.1.7.3.

The phrase "an internal proxy method of the [[GetPrototypeOf]] object" makes no sense. Presumably you mean "the [[GetPrototypeOf]] internal method of a proxy object". And you don't really "use" an invariant, you enforce it, or satisfy it. So you could say: enforcing the invariants from 9.5.1 ensures the enforcing of (two) invariants from 6.1.7.3. But I don't know what you mean by "You can visually show the dependency".

And seems this quote:

Like I said before: this Note is telling you that the [[GetPrototypeOf]] internal method for proxies enforces the [[GetPrototypeOf]] invariants that are specified (in 6.1.7.3) for all objects.

refers to the quote above. Right?

Something I said earlier can't "refer to" something I said later, but they are talking about the same thing.

Next, your sentence:

But yes, the invariants in the Note in 9.5.1 directly correspond to the [[GetPrototypeOf]] invariants in 6.1.7.3.

What do you mean by directly corresponding?

  • The first invariant in the Note in 9.5.1 tells you that the [[GetPrototypeOf]] internal method for proxies satisfies the first of the [[GetPrototypeOf]] invariants in 6.1.7.3.

  • The second invariant in the Note in 9.5.1 tells you that the [[GetPrototypeOf]] internal method for proxies satisfies the second of the [[GetPrototypeOf]] invariants in 6.1.7.3.

Proxy objects are free to maintain additional or more specific invariants, but they are definitely still obliged to maintain the invariants specified in 6.1.7.3.

If we are talking about the internal proxy method of the object [[GetPrototypeOf]] -

(Again, that wording makes no sense. It's the [[GetPrototypeOf]] internal method of a proxy object. The fact that you made the same mistake twice suggests to me that there's something wrong with your mental model of objects and internal methods. You should maybe fix that first before trying to understand invariants.)

are the invariants in the note from 9.5.1 additional invariants or specifying for 6.1.7.3? If these invariants are specifying (9.5.1), then it turns out that they are also invariants that maintain 6.1.7.3? Right?

I'm not sure what you mean by "specifying for 6.1.7.3". Maybe "specializations of 6.1.7.3"?

For the first invariant, the one in 9.5.1 is just a rewording of the one in 6.1.7.3.

For the second invariant, it's not simply a rewording. The one in 9.5.1, because it refers to the proxy object's target object, is specific to proxy objects. It says what the proxy object's [[GetPrototypeOf]] internal method does to enforce the corresponding 6.1.7.3 invariant. So in this case, the 9.5.1 invariant is, in some sense:

  • additional to,
  • a specialization of, and
  • a means to maintain

the corresponding 6.1.7.3 invariant. So I don't know what that means in terms of the distinction you were drawing.

If I understood everything correctly, then taking any proxy object we are obliged to follow the rules of
6.1.7.3, but not required to follow note 9.5.1 (since the note is a non-normative).

Yes. Mind you, following everything else in 9.5.1 will cause you to follow the note as well (i.e., satisfy the invariants in the note).

I correctly understand that 9.5.1 (note) is the same as 6.1.7.3, but 9.5.1 (note) is a specify for 6.1.7.3 of the internal proxy method [[GetPrototypeOf]]?

Some of the wording is unclear, but it's possible that's correct.

(But if you mean that the invariants in 9.5.1 (note) are a specification for the [[GetPrototypeOf]] internal method for proxies, that's incorrect.)

@dSalieri
Copy link
Author

@jmdyck

The phrase "an internal proxy method of the [[GetPrototypeOf]] object" makes no sense. Presumably you mean "the [[GetPrototypeOf]] internal method of a proxy object".

Of course, sorry for my wrong wording.

Something I said earlier can't "refer to" something I said later, but they are talking about the same thing

That's right, I meant that you are talking about the same thing.

(Again, that wording makes no sense. It's the [[GetPrototypeOf]] internal method of a proxy object. The fact that you made the same mistake twice suggests to me that there's something wrong with your mental model of objects and internal methods. You should maybe fix that first before trying to understand invariants.)

No, I thought it wrong, I agree with your assumption above what I meant.

I'm not sure what you mean by "specifying for 6.1.7.3". Maybe "specializations of 6.1.7.3"?

I do not know how to say this correctly on English. But most likely I meant concrete or precise or specialize, if for you the word specifying has a different shade of meaning.
And in this case it should have sounded like: 9.5.1 is a precisement for proxy objects that correspond 6.1.7.3.

Yes. Mind you, following everything else in 9.5.1 will cause you to follow the note as well (i.e., satisfy the invariants in the note).

Hmm...I don't understand you. What does it mean: following everything else in 9.5.1? For me it looks like a sentence(full quote above): if you eat, then you eat - which is meaningless to me.

I correctly understand that 9.5.1 (note) is the same as 6.1.7.3, but 9.5.1 (note) is a specify for 6.1.7.3 of the internal proxy method [[GetPrototypeOf]]?

Some of the wording is unclear, but it's possible that's correct.

Ok, you can simple replace specify on something from that: concrete, precise, specialize. Now more clearly?

When I said:

That is, this note in 9.5.1 shows a feature of invariants for proxies? But in fact these are the same invariants from 6.1.7.3?

by feature I mean this:

Proxy objects are free to maintain additional or more specific invariants, but they are definitely still obliged to maintain the invariants specified in 6.1.7.3.[***]

Where will additional or specific invariants come from if the specification does not specify them?

  • additional to,
  • a specialization of, and
  • a means to maintain

By the way, is this related to the quote above?

P.S I apologize in advance if I again messed up somewhere with the wording.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 28, 2019

I'm not sure what you mean by "specifying for 6.1.7.3". Maybe "specializations of 6.1.7.3"?

I do not know how to say this correctly on English. But most likely I meant concrete or precise or specialize, if for you the word specifying has a different shade of meaning.
And in this case it should have sounded like: 9.5.1 is a precisement for proxy objects that correspond 6.1.7.3.

I think "specialize" is best choice: the second invariant in 9.5.1's note is a (proxy-specific) specialization of the second [[GetPrototypeOf]] invariant in 6.1.7.3. (At least, that's one way to think of it. Some people might think differently.)

Yes. Mind you, following everything else in 9.5.1 will cause you to follow the note as well (i.e., satisfy the invariants in the note).

Hmm...I don't understand you. What does it mean: following everything else in 9.5.1? For me it looks like a sentence(full quote above): if you eat, then you eat - which is meaningless to me.

You aren't required to satisfy the non-normative content of 9.5.1 (the note), but you are obliged to conform to the normative content of 9.5.1 (the algorithm), and in doing so, it will happen that you also enforce the invariants that appear in the note.

Proxy objects are free to maintain additional or more specific invariants, but they are definitely still obliged to maintain the invariants specified in 6.1.7.3.[***]

Where will additional or specific invariants come from if the specification does not specify them?

9.5.1's note has an example of a more specific invariant (or an additional invariant, depending on your point of view). Someone implementing the proxy object internal methods might find additional invariants not stated in the spec. And someone creating a particular proxy object might have invariants in mind when writing the methods of its handler object.

@dSalieri
Copy link
Author

dSalieri commented Jul 29, 2019

@jmdyck

I think "specialize" is best choice: the second invariant in 9.5.1's note is a (proxy-specific) specialization of the second [[GetPrototypeOf]] invariant in 6.1.7.3. (At least, that's one way to think of it. Some people might think differently.)

Good, but I think you will agree that the words I picked up also fit. At least I see their meaning similar to "specialize." (It was about "concrete" or "precise")

You aren't required to satisfy the non-normative content of 9.5.1 (the note), but you are obliged to conform to the normative content of 9.5.1 (the algorithm), and in doing so, it will happen that you also enforce the invariants that appear in the note.

Good clarification, now it became clear.

9.5.1's note has an example of a more specific invariant (or an additional invariant, depending on your point of view). Someone implementing the proxy object internal methods might find additional invariants not stated in the spec. And someone creating a particular proxy object might have invariants in mind when writing the methods of its handler object.

What does it mean:might find additional invariants not stated in the spec? If it is possible to find additional invariants not stated in the specification. So who grants these invariants? Another document that describes the implementation of these invariants (can you give an example of such a document if possible)?

@bathos
Copy link
Contributor

bathos commented Jul 29, 2019

If it is possible to find additional invariants not stated in the specification. So who grants these invariants? Another document that describes the implementation of these invariants (can you give an example of such a document if possible)?

Not all invariants are stated (as such) in the spec mainly in the same sense that when defining what ‘adding two integers’ means, you probably would not bother listing the following invariants about that operation:

  • when the first operand is 1 and the second operand is 1, the result is 2
  • when the first operand is 1 and the second operand is 2, the result is 3
  • [...etc...]

The invariants exist regardless of whether they are stated as such.

Invariants are just things that are, and must remain, reliably true. The explicit invariants for object methods are an aid to implementors who might introduce new exotic objects because when they do this, they may be operating from ‘outside’ of the ES layer.

A summary of all the object invariants in the spec would be (loosely, not precisely) ‘exotic objects must not exhibit behaviors that objects created with ECMAScript source code could not also exhibit’. This is very near to saying ‘all objects must be objects,’ a tautology, but for implementors, having the sum/emergent observable effects of the specified algorithms made explicit is valuable clarification.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 30, 2019

Someone implementing the proxy object internal methods might find additional invariants not stated in the spec.

What does it mean:might find additional invariants not stated in the spec? If it is possible to find additional invariants not stated in the specification.

Sure. For example, here's an invariant:

When [[GetPrototypeOf]] is invoked on a proxy object whose [[ProxyHandler]] is null, it must throw a TypeError exception.

It's not stated as an invariant in the spec, but you can convince yourself that it must hold by looking at the first two steps of the [[GetPrototypeOf]] internal method for proxy objects.

But I think you're going off on a tangent that isn't essential to understanding invariants in the spec.

@dSalieri
Copy link
Author

@jmdyck

Sure. For example, here's an invariant:

When [[GetPrototypeOf]] is invoked on a proxy object whose [[ProxyHandler]] is null, it must throw a TypeError exception.

Hmm, it turns out any step from any algorithm can be written as an invariant. If this is so, then why is something specified as an invariant, but something is not specified (as in your example) What does it make sense?

And judging from this quote:

A conforming implementation of ECMAScript may provide additional types, values, objects, properties, and functions beyond those described in this specification. In particular, a conforming implementation of ECMAScript may provide properties not described in this specification, and values for those properties, for objects that are described in this specification.

It can be concluded that: an object may be created which does not correspond to the invariants from the specification. Does this mean that it follows the invariants that are specified by the implementers of the object? And whether it is connected with:

Proxy objects are free to maintain additional or more specific invariants, but they are definitely still obliged to maintain the invariants specified in 6.1.7.3.

That is, can any proxy objects (or any object) that do not follow the specification define their independent invariants?

But I think you're going off on a tangent that isn't essential to understanding invariants in the spec.

Some details also valueble.

@devsnek
Copy link
Member

devsnek commented Jul 30, 2019

I think it is important to remember that one implementing the spec isn't expected to write their code out in the exact steps listed in the spec. The point of the spec is to convey the behaviour of a conforming implementation. In the case of this invariant you keep bringing up about object behavior, the point is to communicate the intent of the specification, which is something a series of algorithm steps cannot do.

@bathos
Copy link
Contributor

bathos commented Jul 30, 2019

It can be concluded that: an object may be created which does not correspond to the invariants from the specification.

That’s not what that paragraph is saying. It’s saying e.g. Node is free to ship with Buffer as a builtin, or to add new properties to intrinsic objects, etc. It doesn’t concern object internal methods.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 30, 2019

Hmm, it turns out any step from any algorithm can be written as an invariant.

Yes and no. It's probably another non-essential tangent.

If this is so, then why is something specified as an invariant, but something is not specified (as in your example)

If you took even a relatively small algorithm and tried to express all of its normative requirements as a set of invariants, it would be harder to write, harder to read, and harder to implement. Conversely, if you took the invariants of 6.1.7.3 and tried to express them as a set of algorithms, you would probably over-constrain implementations.

And judging from this quote:

A conforming implementation of ECMAScript may provide additional types, values, objects, properties, and functions beyond those described in this specification. In particular, a conforming implementation of ECMAScript may provide properties not described in this specification, and values for those properties, for objects that are described in this specification.

It can be concluded that: an object may be created which does not correspond to the invariants from the specification.

Nope, you can't conclude that. You don't seem to have grasped what @allenwb said 15 days ago:

The "essential invariants of objects" are stated in the spec because they are requirements imposed upon all objects, whether defined by the specification, by an "engine" as extensions to the specification, by an host environment, or by user code.

Please understand that "all objects" means all objects. No exceptions!


Does this mean that it follows the invariants that are specified by the implementers of the object?

If the implementers choose to document the semantics of the object via invariants, they can do so. But regardless, it must satisfy the invariants of 6.1.7.3.

And whether it is connected with:

Proxy objects are free to maintain additional or more specific invariants, but they are definitely still obliged to maintain the invariants specified in 6.1.7.3.

That is, can any proxy objects (or any object) that do not follow the specification define their independent invariants?

It seems to me that the sentence you quoted already answers your question. But in case it helps to have it reworded yet again:

It depends what you mean by "define their independent invariants". If you mean that the total set of invariants they enforce is completely disjoint from the invariants of 6.1.7.3, then no, that's not allowed in a conforming implementation. If you mean that the total set of invariants contains all the ones in 6.1.7.3, and in addition some that aren't in 6.1.7.3, then yes, that's allowed.

(I'm assuming that, by "do not follow the specification", you mean "are not defined by the specification".)

@dSalieri
Copy link
Author

@jmdyck
I understood everything except:

Hmm, it turns out any step from any algorithm can be written as an invariant.

Yes and no. It's probably another non-essential tangent.

Could you clarify why yes and no?

If this is so, then why is something specified as an invariant, but something is not specified (as in your example)

If you took even a relatively small algorithm and tried to express all of its normative requirements as a set of invariants, it would be harder to write, harder to read, and harder to implement. Conversely, if you took the invariants of 6.1.7.3 and tried to express them as a set of algorithms, you would probably over-constrain implementations.

Yes, I agree that invariants are harder to read, write than algorithms and even more so to implement. But you said that if you take the invariants from 6.1.7.3 and try to express them as algorithms, then I would: would probably over-constrain implementations - why, see no reason for this?

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 30, 2019

Hmm, it turns out any step from any algorithm can be written as an invariant.

Yes and no. It's probably another non-essential tangent.

Could you clarify why yes and no?

I maybe could, but since I think it's a non-essential tangent, I'm not going to try.

you said that if you take the invariants from 6.1.7.3 and try to express them as algorithms, then I would: would probably over-constrain implementations - why, see no reason for this?

Well, for instance, consider:

The Type of the return value must be either Object or Null.

How would you convey that requirement as part of an algorithm without specifying which object (or null) must be returned?

@dSalieri
Copy link
Author

@jmdyck

Well, for instance, consider:

The Type of the return value must be either Object or Null.

How would you convey that requirement as part of an algorithm without specifying which object (or null) must be returned?

If I understand correctly, then we can express it as follows:

  • If Type(V) is either Object or Null, return V.

I maybe could, but since I think it's a non-essential tangent, I'm not going to try.

You interested me, please, I would like to get an answer to this.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 30, 2019

* If Type(V) is either Object or Null, return V.

So then the interesting question is: how does the algorithm give V a value?

@dSalieri
Copy link
Author

@jmdyck probably through any steps that should precede this line

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 31, 2019

Of course. But how does it get set in a way that doesn't over-constrain implementations?

@dSalieri
Copy link
Author

dSalieri commented Jul 31, 2019

@jmdyck I do not really understand what is meant by: that doesn't over-constrain implementations
To understand this, I need to at least understand which implementation is over-constrain and which is not.

@jmdyck
Copy link
Collaborator

jmdyck commented Jul 31, 2019

In this context, to over-constrain implementations is to require more of them than the 6.1.7.3 invariants do. I'm saying that if you tried to express the requirements of those invariants as a set of algorithms, those algorithms would probably express additional requirements (constraints on behavior). This could cause some conforming implementations to be deemed non-conforming, breaking backwards-compatibility.

@dSalieri
Copy link
Author

dSalieri commented Aug 2, 2019

@jmdyck

In this context, to over-constrain implementations is to require more of them than the 6.1.7.3 invariants do.

That is, more steps in algorithms in relation to invariants

I'm saying that if you tried to express the requirements of those invariants as a set of algorithms, those algorithms would probably express additional requirements (constraints on behavior). This could cause some conforming implementations to be deemed non-conforming, breaking backwards-compatibility.

That is, for some algorithms, some requirements (features) may arise, in view of which it will be necessary to change the steps of the algorithm to enforce the invariants (and these changes or differences in steps can differ greatly from algorithm to algorithm)?
Therefore, it is more convenient to express requirements through invariants when it covers most algorithms than expressing algorithms through invariants.
It turns out the specification uses the style: over-constrain implementations. Right?

@jmdyck
Copy link
Collaborator

jmdyck commented Aug 2, 2019

In this context, to over-constrain implementations is to require more of them than the 6.1.7.3 invariants do.

That is, more steps in algorithms in relation to invariants

It's not about the number of steps.

I'm saying that if you tried to express the requirements of those invariants as a set of algorithms, those algorithms would probably express additional requirements (constraints on behavior). This could cause some conforming implementations to be deemed non-conforming, breaking backwards-compatibility.

That is, for some algorithms, some requirements (features) may arise,

This isn't about requirements arising from features, it's about requirements being incorrectly introduced as a by-product of trying to replace invariants with algorithms.

in view of which it will be necessary to change the steps of the algorithm to enforce the invariants

I was talking about a hypothetical spec in which the 6.1.7.3 invariants don't appear, so I'm not sure why you're talking about enforcing the invariants.

It turns out the specification uses the style: over-constrain implementations. Right?

No. Over-constraining implementations isn't a style to be used, it's a mistake to avoid.

Anyhow, this whole tangent of hypothetically replacing invariants with algorithms (and why that's a bad idea) is not important to understanding the spec. You might want to think about wrapping up this issue.

@dSalieri
Copy link
Author

dSalieri commented Aug 4, 2019

@jmdyck Well, I agree, you have already answered my main questions. But I would like to separately communicate with you via email or maybe some kind of social network to still find answers to questions that I could not find out here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants