Proposal: Class Templates

# IdkGoodName Vilius (5 years ago)

See: CreatorVilius/ecmascript-proposals/blob/master/proposal-class-templates.md

I think having Class Templates in JavaScript would be awesome thing and should be implemented. This could be useful in a lot of cases. I know that mostly Statically typed languages have templates, but JS could also have Templates. It's not strict that you need to have Statically typed language in order to have Class Templates.

# Claude Pache (5 years ago)

From what I understand (that is, not much), class templates are useful in strongly typed languages, so that one can have a family of classes that share the same implementation but that derive from different types; e.g. Stack<int> for stacks of ints and Stack<string> for stacks of strings. In JS, you can have one Stack class that accepts both ints and strings, so that this use case doesn’t apply (or, at least, it is not a necessity).

But I'm sure there are other use cases. What are they, and are they compelling enough?

# Isiah Meadows (5 years ago)

Reflection would be nice, but that's about the only use case I can think of. Being able to tell a typed number array from a typed object array would be useful for some array processing optimizations and maybe stream processing optimization, but that's about it AFAIK.

# Isiah Meadows (5 years ago)

Also, it's worth mentioning Julia exists as language precedent here: it's got generic runtime types in the form of both classes and type aliases, despite being a dynamically typed, JIT-compiled language.

# Michał Wadas (5 years ago)

Isn't it already solvable by:

const SizedArray = (size) => class SizedArray extends Array {

constructor(...args) { if(args.length > size) throw new SyntaxError('Argument size is too big.') super(...args) } push(i) { if(this.length + 1 > size) throw new SyntaxError('Cannot push

items anymore, array too big.') super.push(i) }

}

const arr = new (SizedArray(5))?

# ViliusCreator (5 years ago)

“Strongly typed” Strongly typed is not Statically typed. Python is strongly typed for example. Strongly typed language means that ”something” + 1 will throw error, Weakly typed means that ”something” + 1 will turn 1 into string and add it to something, which will result in something1.

Statically typed language means that once you do Boolean Abc = true, or let Abc: Boolean = true, you can’t change it to number, but you can change it to false, or true.

Did you mean Statically typed language?

Also, I think that there can be usefulness in class templates, without language being Statically typed.

# ViliusCreator (5 years ago)

Yes, you can do that. But then doesn’t that look ugly? new (Abc(1, 2, 3))(4, 5, 6), vs new Abc<4, 5, 6>(1, 2, 3).

# T.J. Crowder (5 years ago)

On Wed, Jan 16, 2019 at 1:01 PM ViliusCreator <viliuskubilius416 at gmail.com> wrote:

Yes, you can do that. But then doesn’t that look ugly?

new (Abc(1, 2, 3)(4, 5, 6), vs new Abc<4, 5, 6>(1, 2, 3).

You meant new (Abc(4, 5, 6))(1, 2, 3) vs. new Abc<4, 5, 6>(1, 2, 3), right? There's not a lot in it... :-)

The problem you face is that new Abc<4>(1, 2, 3) (where there's only one thing within the <>, presumably a common case) is already valid syntax:

  • new Abc<4>(1, 2, 3) becomes
  • o<4>(1, 2, 3) becomes
  • b1>(1, 2, 3) becomes
  • b1>3 becomes
  • b2

...where o is the object created by new Abc (parens are optional in new if there are no arguments), b1 is the boolean resulting from o<4, and b2 is the boolean resulting from b1>3.

Even though nearly-nonsensical, assigning valid syntax new meaning is a very high barrier to jump. (Interestingly, if weren't the case that < after new Identifier is already valid syntax, the spec changes to enable making new Abc<x> call Abc with x and then use new on the result [no need to tie it specifically to some "class templates" concept] would be fairly small [tagged templates already work that way]. But...) You'd need new Abc<|4|> or similar, and...that seems like it's unlikely to happen.

I want to reach for tag functions here, but it would be either ugly:

new Abc`${4}${5}${6}`(1, 2, 3)

...or an awful and limited hack...

new Abc`4, 5, 6`(1, 2, 3)

(parsing the "arguments" in the template from the strings array passed to the tag function).

new (Abc(4, 5, 6))(1, 2, 3) starts looking pretty good. :-)

-- T.J. Crowder

# Claude Pache (5 years ago)

Le 16 janv. 2019 à 13:57, ViliusCreator <viliuskubilius416 at gmail.com> a écrit :

“Strongly typed” Strongly typed is not Statically typed. Python is strongly typed for example. Strongly typed language means that ”something” + 1 will throw error, Weakly typed means that ”something” + 1 will turn 1 into string and add it to something, which will result in something1.

Statically typed language means that once you do Boolean Abc = true, or let Abc: Boolean = true, you can’t change it to number, but you can change it to false, or true.

Did you mean Statically typed language?

Also, I think that there can be usefulness in class templates, without language being Statically typed.

I meant ”statically typed”, yes. But I think you understood what I meant. My question was about compelling use cases in JS. We know that you think it is useful, but you should convince others that it is useful, that is to say, worth the added complexity in the language.

# Augusto Moura (5 years ago)

In the proposal:

class SizedArray<size> extends Array {

  constructor(...args) {
    if(args.length > size) throw new SyntaxError('Argument size is too big.')
    super(...args)
  }
  push(i) {
    if(this.length + 1 > size) throw new SyntaxError('Cannot push items anymore, array too big.')
    super.push(i)
  }
}

Usage: Usage is simple, and obvious.

// ...
let somethingElse: A<Number> = new A<Number>;

I don't think the syntax is obvious. What will happen in the if inside the constructor? What value size would receive? Infinity? undefined? The Number function? That's no way templates will ever work like that in Javascript, it doesn't makes any sense, C++ templates were implemented to support generic static allocation and safe typings, neither of this features applies to Javascript (and probably never will). Your example could easily be written with constructor arguments (like any sane OOP language), that's not a problem to be solved here

class SizedArray extends Array {
  constructor(size, ...args) {
    if (args.length > size) throw Error();
    super(args);
  }
}
# ViliusCreator (5 years ago)

You could use

new Abc`abc`(‘abc’)

But you could only use strings. What about constructors? You would need to use

const thatConstructor = (new Function(‘return ’ + t))()

But it would only give what is defined in that constructor.


This email has been checked for viruses by Avast antivirus software. www.avast.com/antivirus

# ViliusCreator (5 years ago)

Also, using new (Abc(4, 5, 6))(1, 2, 3) causes error Uncaught TypeError: Abc(...) is not a constructor.


This email has been checked for viruses by Avast antivirus software. www.avast.com/antivirus

# Jordan Harband (5 years ago)

michaelficarra/proposal-first-class-protocols may be a more palatable approach here.

# T.J. Crowder (5 years ago)

On Wed, Jan 16, 2019 at 5:29 PM ViliusCreator <viliuskubilius416 at gmail.com> wrote:

You could use

new Abc`abc`(‘abc’)

I think you must have missed my earlier reply, which covered that, and also the fact that the major problem with using <> for this is that it's already valid syntax (in a common use case) meaning something else (something almost nonsensical, but valid nonetheless).

On Wed, Jan 16, 2019 at 5:41 PM ViliusCreator <viliuskubilius416 at gmail.com> wrote:

Also, using new (Abc(4, 5, 6))(1, 2, 3) causes error Uncaught TypeError: Abc(...) is not a constructor.

Not if Abc is written correctly, for instance like Michał Wadas did with SizedArray earlier in this thread. Abc example:

const Abc = (a, b, c) => {
    // ...optionally do something with a, b, and c...
    // Return a class
    return class Abc$ {
        constructor(x, y, z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        // ...optionally do something with a, b, and c...
        show() {
            console.log(`a = ${a}, b = ${b}, c = ${c}`);
            console.log(`x = ${this.x}, y = ${this.y}, z = ${this.z}`);
        }
    };
};
const obj = new (Abc(4, 5, 6))(1, 2, 3);
obj.show();

jsfiddle.net/gh3dv297

-- T.J. Crowder

# ViliusCreator (5 years ago)

But then Abc(4, 5, 6) !== Abc(4, 5, 6). Using Abc.constructor = class Abc$ {} and then returning Abc.constructor won't make it work.

# Ranando King (5 years ago)

It can still be made to work...

const Abc = (function() {
    const cache = new Map;
    function readCache(c, arg, val=new Map) {
        if (!c.has(a))
            c.set(a, val);

        return c.get(arg);
    }
    return (a, b, c) => {
        // ...optionally do something with a, b, and c...
        // Return a class
        let retval = class Abc$ {
            constructor(x, y, z) {
                this.x = x;
                this.y = y;
                this.z = z;
            }

            // ...optionally do something with a, b, and c...
            show() {
                console.log(`a = ${a}, b = ${b}, c = ${c}`);
                console.log(`x = ${this.x}, y = ${this.y}, z = ${this.z}`);
            }
        };

        return readCache(readCache(readCache(cache, a), b), c, retval);
    };
})();
const obj = new (Abc(4, 5, 6))(1, 2, 3);
obj.show();

Now Abc(4, 5, 6) === Abc(4, 5, 6).