An "extend" operator is a natural companion to <|

Bob Nystrom rnystrom at google.com
Tue Jul 19 11:07:18 PDT 2011


On Mon, Jul 18, 2011 at 9:09 PM, Brendan Eich <brendan at mozilla.com> wrote:

> On Jul 18, 2011, at 9:02 PM, Rick Waldron wrote:
>
> Hey Bob, FWIW...
>
>
>> class Point {
>>   constructor(x, y) {
>>     this.x = x;
>>     this.y = y;
>>   }
>>
>>   zero() {
>>     return new Point(0, 0);
>>   }
>>
>>   unit() {
>>     return new Point(1, 1);
>>   }
>>
>>   prototype {
>>     manhattanDistance() {
>>       return Math.abs(this.x) + Math.abs(this.y);
>>     }
>>   }
>> }
>>
>
> ...That's actually the nicest, most intuitively designed "class" structure
> I've seen so far.
>
>
> This is actually close to a less magical syntax that flips around the
> "ClassElements" (immediate children of the class {...} container) to specify
> class methods. Here's a less sugared version:
>
> class Point {
>>   zero() {
>>     return new Point(0, 0);
>>   }
>>
>>   unit() {
>>     return new Point(1, 1);
>>   }
>>
>>   prototype: {
>>     constructor(x, y) {
>>       this.x = x;
>>       this.y = y;
>>     }
>>
>>     manhattanDistance() {
>>       return Math.abs(this.x) + Math.abs(this.y);
>>     }
>>   }
>> }
>>
>
>>
> Notice how constructor is in the prototype object, as in JS with
> constructors and prototypes. Also, prototype: {...} not prototype {...}.
>
> As Bob noted, though, even in JS and moreso (from Allen's stats) in
> Smalltalk, class methods are uncommon compared to prototype methods. Do you
> really want an extra level of indentation for the latter?
>
> This example is skewed away from norms by having two class methods to one
> prototype method. I think constructor was mislocated, and when I fixed it
> just above, the instance- vs. class-method counts matched. Still
> unrealistic, though.
>
> If we want *more* magic from the class syntax, not less, we could use 'new'
> at the outer class-element indentation level to declare the constructor, and
> automagically impute 'constructor' in the prototype from it. But we'd still
> be over-indenting the prototype methods, which are many, compared to the
> class methods (few).
>

This is probably a terrible idea, but in the interest of considering all
options, we could do something like:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

class:
  zero() {
    return new Point(0, 0);
  }

  unit() {
    return new Point(1, 1);
  }

prototype:
  manhattanDistance() {
    return Math.abs(this.x) + Math.abs(this.y);
  }
}

So, class: and prototype: indicate that members declared after them go into
those objects, much like public: and private: in a class in C++. By default,
it assumes prototype: since that's where most members go. In this example
here, I'm switching back and forth just to show the idea, but idiomatic code
would likely be:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  manhattanDistance() {
    return Math.abs(this.x) + Math.abs(this.y);
  }

class:
  zero() {
    return new Point(0, 0);
  }

  unit() {
    return new Point(1, 1);
  }
}

(It may be that we should use constructor: or static: instead of class:.)

That solves the double indentation problem. We could possibly extend this to
handle declaratively specifying instance members and/or public/private too:

class Monster {
  constructor(name, health) {
    this.name = name;
    this.health = health;
  }

  attack(target) {
    log('The monster attacks ' + target);
  }

  // The contextual keyword "get" followed by an identifier and
  // a curly body defines a getter in the same way that "get"
  // defines one in an object literal.
  get isAlive() {
    return this.health > 0;
  }

  // Likewise, "set" can be used to define setters.
  set health(value) {
    if (value < 0) {
      throw new Error('Health must be non-negative.')
    }
    this.health = value
  }

// "new" lets you declare members on new instances. these would presumably
be
// invoked on the new object before the constructor body is run.
new:
  numAttacks = 0;
  // declaring an instance property here mainly so you can document it.
could be
  // useful later for guards or other annotations.
  name;

// "private" before a section lets you declare private members on that
object.
private new:
  health;

class:
  create(name) {
    let health = Math.random(5, 20);
    return new Monster(name, health);
  }

  const attackMessage = 'The monster hits you!';
}

Strangely, I don't find myself hating this as much as I expected. <shrug>

- bob
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110719/5af2e157/attachment-0001.html>


More information about the es-discuss mailing list