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

decide what qualifies as a "field" #164

Closed
davidchambers opened this issue Jul 6, 2017 · 1 comment
Closed

decide what qualifies as a "field" #164

davidchambers opened this issue Jul 6, 2017 · 1 comment

Comments

@davidchambers
Copy link
Member

davidchambers commented Jul 6, 2017

In recent days it has become apparent that we need to give more thought to the question of what qualifies as a record field. It's absolutely clear that {email: '[email protected]'} has an email field, but does it have a toString field? This is not so easy to answer.

I see four possibilities, as shown in the table below:

+----------------------------+-----------+-----------+
| property location          |    own    |   proto   |
+----------------------------+-----+-----+-----+-----+
| enumerable?                |  y  |  n  |  y  |  n  |
+----------------------------+-----+-----+-----+-----+
| Object.keys                |  x  |     |     |     |
| Object.getOwnPropertyNames |  x  |  x  |     |     |
| getEnumerablePropertyNames |  x  |     |  x  |     |
| prop in o                  |  x  |  x  |  x  |  x  |
+----------------------------+-----------+-----------+

Here's the script which generates the table:

'use strict';

function O() {}
O.prototype.enumerablePrototypeProperty = 'enumerablePrototypeProperty';
Object.defineProperty(O.prototype, 'nonEnumerablePrototypeProperty', {
  configurable: false,
  enumerable: false,
  writable: false,
  value: 'nonEnumerablePrototypeProperty',
});

const o = new O();
o.enumerableOwnProperty = 'enumerableOwnProperty';
Object.defineProperty(o, 'nonEnumerableOwnProperty', {
  configurable: false,
  enumerable: false,
  writable: false,
  value: 'nonEnumerableOwnProperty',
});

const _includes = Array.prototype.includes;

process.stdout.write(
  '+----------------------------+-----------+-----------+\n' +
  '| property location          |    own    |   proto   |\n' +
  '+----------------------------+-----+-----+-----+-----+\n' +
  '| enumerable?                |  y  |  n  |  y  |  n  |\n' +
  '+----------------------------+-----+-----+-----+-----+\n' +
  '| Object.keys                |' + test(_includes.bind(Object.keys(o))) + '\n' +
  '| Object.getOwnPropertyNames |' + test(_includes.bind(Object.getOwnPropertyNames(o))) + '\n' +
  '| getEnumerablePropertyNames |' + test(_includes.bind(getEnumerablePropertyNames(o))) + '\n' +
  '| prop in o                  |' + test(prop => prop in o) + '\n' +
  '+----------------------------+-----------+-----------+\n'
);

function getEnumerablePropertyNames(o) {
  const props = [];
  for (const prop in o) props.push(prop);
  return props;
}

function test(pred) {
  return [
    'enumerableOwnProperty',
    'nonEnumerableOwnProperty',
    'enumerablePrototypeProperty',
    'nonEnumerablePrototypeProperty',
  ].map(prop => '  ' + (pred(prop) ? 'x' : ' ') + '  |').join('');
}

We must choose from these four definitions:

  • A field is a property.
  • A field is an own property.
  • A field is an enumerable property.
  • A field is an enumerable own property.

We should consider "strict" record types (#128) before making a choice.

@Avaq and I have discussed the possibility of strict records having a different definition of field from the one chosen for regular records. This is potentially problematic. Imagine we were to decide that a field is an enumerable property, and that we proceeded to write the following:

function HorizontalLine(x, y, dx) {
  this.x = x;
  this.y = y;
  this.dx = dx;
}
HorizontalLine.prototype.dy = 0;

const LineType = $.RecordType({
  x: $.FiniteNumber,
  y: $.FiniteNumber,
  dx: $.FiniteNumber,
  dy: $.FiniteNumber,
});

const line = new HorizontalLine(1, 2, 3);

line is member of LineType because it has all the required enumerable properties, each with a suitable value.

Imagine that for strict records we decided that a field is an own enumerable property. This would mean that line is not a member of the strict equivalent of LineType because it lacks a dy field.

If possible, we should use the same definition of field for both regular and strict records so a member of a record type is only excluded from the equivalent strict record type for having additional fields.

I don't think it's possible to list all of an object's properties regardless of where and how they are defined (please correct me if I'm wrong). As a result, prop in o cannot be used to define strict record types as we would need to test an infinite number of possible property names to ensure that no unwanted fields are present. For consistency, we should avoid it for regular record types as well.

This leaves three definitions:

  • A field is an own property.
  • A field is an enumerable property.
  • A field is an enumerable own property.

It would be a shame not to support prototypal inheritance, as I consider it one of the good parts of JavaScript. I suggest we state that a field is an enumerable property. Note that this means that Promise.resolve(42) is not a member of $.RecordType({then: $.AnyFunction}) as @JAForbes requested in #141. I can live with this. One could define an equivalent type via $.NullaryType (or even $.UnaryType).

@Avaq
Copy link
Member

Avaq commented Jul 10, 2017

+1 for a field is an enumerable property

I will update #162 accordingly while we wait for input from the community.

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

No branches or pull requests

2 participants