extends keyword instead of <superclass ... >

Dmitry A. Soshnikov dmitry.soshnikov at gmail.com
Wed Mar 30 03:51:54 PDT 2011


On 30.03.2011 1:24, Allen Wirfs-Brock wrote:
>
> On Mar 29, 2011, at 12:14 AM, Dmitry A. Soshnikov wrote:
>
>> On 29.03.2011 2:51, Allen Wirfs-Brock wrote:
>>> ... 
>>
>> Regarding classes in general I have the following table of "classes" 
>> kinds:
>>
>>         |    first-class                 |   second-class (or 
>> "first-order")
>> --------|------------------------------- 
>> |-----------------------------------
>> dynamic |    Ruby, Python, JS, Coffee    |         ?
>> static  |    freeze(class)               |      C++, Java
>>
>> Thus, combination of "statics + second-class" can give us an 
>> immutable type with strong predefined behavior and set of properties.
>
> FWIW,  according to this model, Smalltalk has a static, first-class 
> classes and I believe most of your points below also apply to such 
> languages
>

Yes, I believe it's so. "First-class" property doesn't cancel the fact 
that a class is equals to type. First-class / Second-class are just 
about the ability to use the class as a value (to pass as arguments, 
return, etc), no more, no less.

Actually, the borders of all these definition are smoothed. E.g. 
Python's classes (and it has first-class dynamic/static classes) are 
also just a syntactic sugar over the delegation-based inheritance 
(nearly the same of what is planned in this strawman). But Python names 
its classes as types (according to `type` operator).

class A(object):
     pass

a = A()

print(type(a) == A) # True

# the class is a first-class value

def foo(Class):
     print(Class)

# i.e. we may e.g. pass it as argument

foo(A) # <class '__main__.A'>

# the (user) class is dynamic
# we may extend it with a new prototype property

A.x = 10;

# and already existing instance
# has the access to this new property

print(a.x) # 10, via delegation

# and we may shadow it with own property

a.x = 20 # own property

print(a.x) # 20, own

del a.x # remove own

print(a.x) # again 10, via delegation from "prototype" - class

So I think we should mostly concentrate on real practical application of 
what we want to set/get from "classes as sugar" and less on some 
theoretical things (which though also important in the design of a 
language). This is how I see it of what we need:

We need the sugar for better classified programming. I.e. we need an 
ability to conveniently describe a _shape_ (a pattern) of objects which 
will have this shape, which will be generate by this pattern (by this 
classification). Thus, assuming dynamics, we may want not to define this 
shape as static. If a user wants to, he/she can mutate classes/objects 
at runtime with monkey-patching.

We may provide some derived functionality for membership and feature 
testing such as property "class" -- for getting the class from the 
instance, "class.super" -- for getting the superclass, etc (involving 
already existing `instanceof`, etc). To stratify meta-level, we may want 
to use Class.getClass(object) instead of object.class (or even better, 
Object.getClass(object) which nearly equal to object.constructor) and 
Class.getSuperclass(aClass) instead ob aClass.super.

The user at the same time is free not to use all these `instanceof`, etc 
checking the hierarchy membership, but instead to use "Duck-testing", 
i.e. a direct feature testing -- if (isFunction(object.read)) { ... OK 
and no matter what's the class of the object ... }. Though, not in all 
cases such tests are acceptable.

>>
>> In dynamic classes first-class classes, a "type" as a set of 
>> predefined and immutable things is not so important. Moreover, for 
>> feature-testing, as a "type-tag" or better to say as a 
>> "classification-tag" can be used just a simple property of an object 
>> which helps to distinguish an object of yours classification from the 
>> object with the same state.
>>
>> foo.class == Foo; // true -- testing with a class-tag
>> foo instanceof Foo; // true
>>
>> It's enough for dynamic first-class classes, and substitution 
>> principle may not be so important. Moreover, even here, e.g. the 
>> following substitution works fine:
>>
>> bar instanceof Bar; // true
>> bar instanceof Foo; // true, assuming that Bar is a subclass of Foo
>>
>> And the set of methods and properties in the dynamic classes of 
>> course can vary over the time. And of course in such a system we 
>> cannot predict whether will be able to substitute an instance after 
>> some mutations (removing/addition methods, etc). But repeat, it's not 
>> so required hard in the dynamic system. But if you still want be 
>> sure, the just make them completely frozen (i.e. static classes) and 
>> then you can be sure.
>>
> There is much Smalltalk experience that shows that "class testing" 
> leads to code that is less maintainable or  less reusable.  Smalltalk 
> style guides (for example see 
> http://stephane.ducasse.free.fr/FreeBooks/WithStyle/SmalltalkWithStyle.pdf ) 
> invariably contained items like "don't test for a specific class" and 
> "don't use isKindOf: or isMemberOf:"  (tests for inherited/direct 
> class membership).  The reason is that you should care more about the 
> behavioral contract of an object rather than which specific 
> implementation class it is an instance of  as there may be multiple 
> classes whose instances are behaviorally interchangeable from some 
> specific perspective.

Yes, this exact "Duck-testing" or "programming with Duck-typing" 
mentioned above.

> Behavior tests such as classification properties of the sort you 
> mention are fine.  Implementation class tests such as instanceof are 
> not, even though as a practical matter they need to be available.
>

Yes, but such an ability can be also needed, so we shouldn't exclude it.

>> So what is more important here (and exactly about it is your strawman 
>> as I understand, right?) is the syntactic sugar for exactly 
>> _convenient classified programming_. For the convinient classified 
>> generation of objects created by the specified (classified) 
>> _patterns_. And exactly from the _convenience of the usage_ of such a 
>> sugar I think we should start.
>
> One of the things I was trying to say was that the most important 
> thing about syntactic class definitions is their  "modularity".  They 
> group all the elements that define the a set of objects with a common 
> implementation into a single modular unit.

Of course, but this can be achieved already today. With simple 
wrapper-function which encapsulates all this desugared stuff by linking 
prototypes, defining helper meta-properties, etc. We may even freeze the 
class (and probably even all instances). That's why we need exactly the 
_syntactic_ _sugar_ for this. Because, as I mentioned, without the 
sugar, we already have it.

>  The class declaration is the "module" that provides this grouping. 
>  The fact that you can use class membership for classification 
> purposes is secondary and as mentioned above often undesirable.
>

Yes, of course I understand that the main thing is exactly the 
generation of the objects with (initially) the same structure. Though we 
shouldn't exclude inheritance as code reusing in the classified 
programming. Btw, there also modules are planned (already prototyped by 
Dave Herman in Narcissus). We may use them to provide horizontal 
inheritance as I mention. Via `include` or proposed `uses` in the 
previous letter. I.e. a module can implement some independent shared 
behavior (and every class may include this module augmenting instance 
method). It's like interfaces in Java.

>>
>>> One of the rules of object subtyping is that additional  methods may 
>>> be added by a subtype but methods may not be deleted.  Thus in a 
>>> subclassing==subtyping language it is easy to think about subclasses 
>>> as generally "extending" superclasses with additional members.  The 
>>> use of "extends" in Java is no doubt a reflection of that perspective.
>>>
>>
>> Yes, that's true, but it seems a little bit as a nit-picking to exact 
>> syntax/terminology. Instead, we should concentrate on exactly the 
>> _convenient code reuse_ and _convenient classified generation_. 
>> Repeat, it doesn't matter much in dynamic system whether we'll remove 
>> some method (and therefore we shouldn't use keyword "extends"). If 
>> you just don't like exactly this keyword (assuming statics and only 
>> extension, not modification of descendant classes) then we may use 
>> any other word which you think fits better. E.g. `inherits` -- class 
>> B inherits A. Or symbols -- yours, proposed on Twitter (C++'s 
>> actually) colon : -- class B : A. Or Ruby's one -- class B < A (which 
>> is also logical -- "the class B is less than A").
>>
>> But I think exact keyword isn't so important in this case. `extends` 
>> keyword is just familiar -- yes, from Java's syntax (actually 
>> JavaScript uses Java's syntax). And it doesn't matter and a Java 
>> programmer doesn't know that a class in JS can be dynamic and that 
>> after the extension there can be other modifications.
>
> I agree with you that the specific syntax for specifying the 
> "superclass" is secondary and it wouldn't be the end of the word if we 
> used the keyword "extends".  But I did want point out that its 
> familiarity may carry additional baggage.

Yes, I see your point, though also mentioned, this difference can be 
smoothed in different implementation (again -- the example with Python 
above).

>   I also think there is some tension in this community between those 
> who would prefer a "second-class static" subtyping based class model 
> and those who are quite content with a "first class dynamic or static" 
> inheritance based class model.  If we are going to provide a class 
> syntax we need to balance those perspectives  or at least understand 
> which we are favoring in our design decisions.

Of course. Though, "second-class'ness" in probably has nothing to do in 
JS since it operates everywhere with first-class entities. Even Dave's 
modules being static are still first-class. So I thing we should 
consider statics vs. dynamics. Yes, we may make all classes by default 
static. And to create a dynamic class, the user will use `dynamic` 
keyword explicitly. Yes, like in ActionScript.

Btw, regarding ActionScript. Maybe we here invent the wheel and it's 
better to reuse ActionScript's syntax and classes semantics? Probably 
not completely, but the most part. After all, it initially is also based 
on ECMA-262.

>> ...
>>
>> What I want is to provide more convenient programming. What exactly 
>> you don't like in `@` as `this`? Isn't it convenient to describe 
>> class methods as just " (1) `this` evaluated in the class body -- is 
>> the class. (2) `this` evaluated inside instance method -- is an 
>> instance". And do you think the sugar I described is a good 
>> declarative form of classes sugar (or if not -- what did I do wrong?) ?
>
> I think we already have "this" and it is too late to replace it with 
> "@" in expressions.
>

Yes, @ may seem just non-habitual, and moreover I propose not to replace 
`this` but to make @ as the _alias_ for this. However, it was just a 
variant. I received other syntax proposal on Twitter which are more fit 
to Java's / JavaScript's syntax:

E.g. (please take a look):

https://gist.github.com/893157
https://gist.github.com/892109
https://gist.github.com/892470 (this one uses similar to yours syntax)

(and that my again http://bit.ly/hvS7hl as addition for comparison) -- I 
repeat the rule which I used: (1) `this` evaluated inside the class body 
-- is the class itself, (2) `this` evaluated in the instance method -- 
is the instance. (3) use @ as alias for `this`. And I think it's not 
convenient (short and elegant), but it's damned convenient.

Though, I won't be against if @ really seems very non-habitual and 
`this.` is more convenient for them. If so, then I still would like to 
see @ or # as meta-properties in initialisers (notice, it's only for 
initialisers, it won't be possible to change it e.g. via foo. at proto = 
...; only initial define).

>>
>> Writing every time "class method", "class method", "class method" not 
>> only too long (and therefore boring), but also will highlight this 
>> `class` keyword many times (which will annoy the users).
>
>
> I think we can probably get rid of the "method".  "class" doesn't 
> bother me as being an excessively heavy weight of designate the 
> relatively rare occurrences of constructor properties.

`static` keyword then fits better than `class` IMO.

static foo: function (x) {
    ...
}

static x: 10

or just:

static foo() {...}

but not so convenient for properties:

static var x = 10

or static x = 10

I used exactly object-initialiser pattern to make it really look like an 
declarative initialiser (and syntactically similar to object initialiser 
itself), so:

class Point {
   static foo: function () {
     ...
   }
   static x: 10,
   bar: function () {}
}

or in mine:

class Point {
   @foo: function () {
     ...
   }
   @x: 10,
   bar: function () {}
}

>   Over all, I think this community tends to over use the need to 
> minimize typing argument.  Most lines of code are read many more times 
> than they are written so there is a good case to be made for 
> preferring readability or writability. Constructs that are used 
> relatively infrequently should be more biased towards supporting 
> readability while writability matters more for very frequently used 
> constructs.
>
Yeah, true.

Dmitry.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110330/16249599/attachment-0001.html>


More information about the es-discuss mailing list