microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.19k stars 12.38k forks source link

the typeclass model offers superior extensibility #10844

Closed shelby3 closed 7 years ago

shelby3 commented 8 years ago

Subclassing inheritance is an anti-pattern (see also this and this and this).

For those who aren't familiar with nominal typeclasses in for example Rust and Haskell, they conceptually (without getting into details and caveats yet) are a way of adding implementation to existing nominal types without modifying the source for those existing nominal types. For example, if you have an instance of existing nominal type A and your want to invoke a function that expects a nominal type B input, typeclasses conceptually enable you to declare the implementation of type B for type A and invoke the said function with the said instance of type A as input. The compiler is smart enough to automatically provide (the properties dictionary data structure or in ECMAScript the prototype chain) to the function the type B properties on the type A instance.

The has efficiency, SPOT, and DNRY advantages over the alternative of manually wrapping the instance of A in a new instance which has B type and delegate to the properties of A. Scala has implicit conversion to automate, but this doesn't eliminate all the boilerplate and solve the efficiency (and tangentially note Scala also can implement a typeclass design pattern employing implicits). This disadvantage of wrapping with new instances compared to typeclasses is especially significant when the instance is a collection (or even collection of collections) of instances (potentially heterogeneous with a union type), all of which have to be individually wrapped. Whereas, with typeclasses only the implementations for each necessary pair of (target, instance) types need to be declared, regardless of how many instances of the instance type(s) are impacted.

And afaics this typeclass model is what the prototype chain in EMCAScript provides.

When we construct a new instance, the A.prototype of the constructor function A is assigned to the instance's protoype, thus providing the initial implementation for the instance (of type A) of the properties of type A. If we want to add an implementation of constructor function B (i.e. of type B) for all instances of type A, then we can set A.prototype.prototype = B.prototype. Obviously we'd like to type check that A is already implemented for B, so we don't attempt to implement B for A thus setting B.prototype.prototype = A.prototype creating a cycle in the prototype chain.

That example was a bit stingy thus contrived, because actually we'd want a different implementation of type B for each type we apply it to. And afaics this is exactly what typeclasses model.

I am very sleepy at the moment. When I awake, I will go into more detail on this proposal and try to justify its importance, relevance to TypeScript's goals, and important problems it solves.

Note I had recently discovered in my discussions on the Rust forum in May what I believe to be a complete solution to Wadler's Expression Problem of extensibility (the O in SOLID), which requires typeclasses and first class unions (disjunctions) and intersections (conjunctions).

There are 500+ detailed comments of mine (and @keean) over there (~335 of which are private) I need to reread and condense into what I want to say in this issue proposal. And it was to some extent and unfinished analysis that I had put on the back burner. I have elevated this priority seeing that TypeScript has the first-class unions and intersections and seeing that upcoming 2.1 is supposed to look into the nominal typing issue for #202.

I have mentioned typeclasses in a few of my recent comments on TypeScript issues.

shelby3 commented 7 years ago

@keean I am thinking (by now) it is better to see if N4JS will consider adding typeclasses. They are aiming for a more sound typing system and they have more incentive to do something to be different. Please also feel free to enjoin the discussion over there.

However, it doesn't hurt to have discussed over here. This discussion here can serve as a reference point for other discussions else where.

I think it is also good to have helped elucidate to readers here about these issues.

I don't entirely understand the claim that TypeScript doesn't emit any boilerplate, as it clearly does if you look at the source code of the compiler. I think @RyanCavanaugh is differentiating it on not deriving boilerplate from typing decisions, because TypeScript's typing engine is somewhat unsound and unpredictable. So they prefer to punt to "programmer error" and maintain the spirit of JavaScript as an untyped Wild West. I do sort of see the point of their strategy. Typing in TypeScript are supposed to be erasable hints. They are aiming for heuristic interopt with the unsafe world of JavaScript. Any way, we know that there is no programming that is 100% safe. So I am not being dogmatic about it. I'll try to better understand the TypeScript way of interopting with JavaScript's ecosystem.

shelby3 commented 7 years ago

Perhaps we should review how Swift applies typeclasses.

shelby3 commented 7 years ago

@SimonMeskens wrote:

This thread is becoming more and more like a Dali painting and less like constructive discussion. Good luck with trying to comprehend this episode of Doctor Who

I thought I couldn't reply directly to the above quote because on the surface it appears to not be technical (and I probably didn't realize it was humor when I first "read" it trying my best to not really read it to not get offended), but then while writing a comment on another issue, I read the following from a man with a 166 IQ which I think might be an applicable technical explanation:

BTW: from my humble, average-IQ perspective, there are no noticeable intellectual inequalities between you, your blog’s regulars (excluding yours truly), Yudkowsky, Von Neumann, etc. :-)

Unsurprising. I too can only perceive one undifferentiated stratum above the one I’m in; it’s entirely possible that this is because I’m not noticing distinctions that are functionally important and would be apparent to me if I weren’t so dimwitted.

I don’t really know what happens at that upper boundary – something subtle but important, analogous to polymathy but different. If I were a high-grade, Tao/von-Neumann-class genius I might be able to pin it down and explain it; as it is, I’m nearly as much at a loss as you are. All I actually know is that there are people whose mental processes I cannot follow, and they produce work of a brilliance I will never match.

That doesn’t seem in the least bit odd to me. Not that I’ll ever have his talent, but I know from experience that when your unconscious mind does a complex cognitive task out of your sight its way of presenting you with the result can be, oh, a Pink Floyd lyric.

So…was Ramanujan brighter than me? Hell to the fuck yeah. Were his mental processes really fundamentally different from mine? When I was younger I would have said “Of course!” without really thinking it all the way through. Now I find a doubt.

But, of course, it’s possible that I don’t understand the mental lives of the stratum above me as well as I think I do.

Anecdote: Just once I’ve had the experience of sitting down to breakfast with an extended family and feeling like the slow kid at the table. The family was Freeman Dyson’s. :-)

shelby3 commented 7 years ago

@RyanCavanaugh well why did TypeScript implemented private member syntax when it is impossible for EMCAScript to ever support runtime safe data encapsulation without massive programmer error (thus not encapsulation because there will be leakage due to this programmer error)?

@shelby3 wrote:

Whoops you have an insoluble design problem for the sigil, unless you decide to reference the public field of obj.field when the private field obj.#field doesn't exist!

So you need to conflate the private and public values on this, otherwise you break encapsulation safety. ;)

There is no way to implement secure data encapsulation for classes in a programming language without incurring massive programming errors in the absence of static typing.

It is an incongruity to say we are going to add a static type system and emit to a dynamically typed runtime, and also claim there will not be any emitted code that is dependent on typing deductions, because you also allow casting away the private restrictions at compile-time (as directed by the code) thus the deduction is to create illegal access to a private member. The only way to reliably (from an ecosystem, holistic perspective) runtime encapsulate private values in JavaScript is to make them never accessible outside the instance (not the class) they are contained in, which isn't the same paradigm as class private lexical scope.

So your response is that TypeScript is allowed to add "type system behavior" functionality that EMCAScript will never be able to add, then typeclasses also fall into the same category, because they can't be implemented (other than as a design pattern) without static typing.

When I wrote there seems to be ambiguity in the design goals of TypeScript, it means I am doing this phenomenon inside my head.

keean commented 7 years ago

@shelby3 I have started a git repo to develop a type-class based language that compiles to JavaScript, with the key objective of being a simple, single paradigm language, supporting the kind of type-driven generic programming covered in "Elements of Programming" by Alexander Stepanov and Paul McJones. I would very much appreciate your input (and also anyone else that cares to contribute), I have chosen an MIT license and all the code and issues are public. I have started an first issue to discuss the big-picture stuff here: https://github.com/keean/traitscript/issues/1

spion commented 7 years ago

It is an incongruity to say we are going to add a static type system and emit to a dynamically typed runtime, and also claim there will not be any emitted code that is dependent on typing deductions, because you also allow casting away the private restrictions at compile-time (as directed by the code) thus the deduction is to create illegal access to a private member. The only way to runtime encapsulate private values in JavaScript is to make them never accessible outside the instance (not the class) they are contained in.

It is just as much an incongruity as, say, type erasure for function arguments. Within TypeScript, there is a restriction that e.g. the argument must be a string. With the type erased in JS, the argument can become anything (string, number, whatever). Illegal calling of the function is now allowed.

Similarly, the restriction that doesn't allow a private field to be accessed except in certain situations exists only in TypeScript. With the annotation erased, there is no restriction to access the member field. Illegal access of the field is now allowed.

Both are compile-time-only restrictions, and both are conceptually exactly the same. They also share the basic property being discussed here: the same JavaScript is going to be emitted with or without them.

shelby3 commented 7 years ago

@spion afaics you miss the point by comparing erasure which doesn't change the emitted code to casting away for example the private to public access (and assuming that private access would require different code if Private Fields proposal becomes a reality) which would change the emitted code. Even casting the class or interface type can change the emitted code in terms of which methods may be called. It is not true that typing in TypeScript doesn't change the code that is emitted or allowed.

I just find the entire topic somewhat pointless and arbitrary. JavaScript isn't compile-time typed. We can't build a sound typed language binding ourselves to never emit any code and restrict ourselves to some (what appears to me to be dubious) notion of interoperability with EMCAScript. Complexity explodes, simplicity and elegance are forsaked. JavaScript is a different language from our sound typed language. We might try to keep correspondence close to JS at the minimum in expression syntax and semantics. So I am moving to @keean's new initiative.

SimonMeskens commented 7 years ago

@shelby3 I think you misunderstand TypeScript. TypeScript doesn't offer type safety, it offers compile time type safety, HUGE difference. The idea is that the compiler promises you that a function is correctly called, not that it can't be incorrectly called.

JavaScript isn't typed.

Of course JavaScript is typed, it just isn't type checked. TypeScript offers a way to do just that, at compile time. If you think a language that only offers compile time safety is useless, feel free to seek another project, just know that most people disagree with you, as evidenced by the fact that TypeScript is one of the fastest growing languages.

The final point of contention is that Javascript DOES allow you to explicitly type check at run-time (another proof that it is typed, how else would that even work?), just not implicitly. TypeScript adds compile time verification of that run-time type check through its flow analyzer.

As for interop, JavaScript is TypeScript, as TypeScript is a superset. Interop isn't dubious, it's not a notion, it works perfectly right now.

In short, you are grossly unknowledgeable about TypeScript AND JavaScript.

spion commented 7 years ago

@shelby3 Thats just how things happen. The TC39 proposal did not exist at the time TypeScript added private fields. That proposal is also called "private fields", but the similarity they share is in name only.

If and when this proposal becomes a part of ES, I suppose TypeScript will have to find a way to deal with the dissonance.

aluanhaddad commented 7 years ago

@SimonMeskens JavaScript is typed, but not in a way that lets you check types at runtime beyond the use of typeof.

shelby3 commented 7 years ago

@SimonMeskens wrote:

it [TypeScript] offers compile time type safety

Incorrect and incorrect.

The idea is that the compiler promises you that a function is correctly called, not that it can't be incorrectly called.

Nope. It can't promise the invocation is correct at the time it is called.

Of course JavaScript is typed, it just isn't type checked.

It is uni-typed at compile-time. The runtime types may or may not be checked.

I've never seen it proved that JS types can be entirely inferred at compile-time without any type declaration. Therefor it is not compile-time typed.

just know that most people disagree with you

Sorry TypeScript's programmer base does not represent most people. And not all of the TypeScript programmers make so many errors in their pronouncements.

The final point of contention is that Javascript DOES allow you to explicitly type check at run-time

Sometimes some types are available, but not always and not all types (as in all the semantic types the programmer has in his head when writing the code).

as TypeScript is a superset

Incorrect.

In short, you are grossly unknowledgeable about TypeScript AND JavaScript.

I have a mirror in my house. How about you?

All of this discussion above can get quite complex. Let me simplify it as follows.

Clever marketing notwithstanding, TypeScript is a language which emits ECMAScript. Choose it if you like it. JavaScript (ECMAScript) is a different language.

spion commented 7 years ago

Even casting the class or interface type can change the emitted code in terms of which methods may be called. It is not true that typing in TypeScript doesn't change the code that is emitted or allowed.

Please go ahead and provide an example 😀

SimonMeskens commented 7 years ago

Almost every word you wrote there is incorrect @shelby3. You're not holding a mirror up, you're showing the world just how correct I was.

Let me explain it to you in simple terms a layman would understand: JavaScript has differential inheritance, it achieves this by storing a reference to a delegation object. Any objects that share the same delegation object are of the same type. This delegation object can be checked at runtime, hence type can be checked at runtime. This is an extremely common pattern in real world JavaScript.

As for TypeScript not being a superset, how is it not? Every valid piece of JavaScript is valid TypeScript and compiling any code that is valid JavaScript results in the compiled output being exactly the same as the input. How is that not a superset?

shelby3 commented 7 years ago

@SimonMeskens wrote:

you're showing the world

Hyperbole.

You're not holding a mirror up

I am also not blind in both eyes.

Any objects that share the same delegation object are of the same type

{ ... } undifferentiated from Object.prototype is uni-typing. As I wrote, "sometimes..."

As for TypeScript not being a superset, how is it not?

I have a different definition of the utility of a superset. I can't take any TypeScript code and run it on JavaScript without compiling. It is a useless point that TypeScript can compile JavaScript, because I don't need TypeScript for doing nothing at all. I think the point you are wanting to make is that the correspondence between TS and JS is tight and the learning curve is slight and gradual. Nevertheless, they are different languages.

SimonMeskens commented 7 years ago

A is superset of B means that any B is valid A, but not every A is valid B. The fact that any B compiles to the exact same B proves this without doubt. This is the case for TypeScript and JavaScript. Also, @spion called you out on an incorrect statement, I dare you to provide the example he asked (hint, you won't find it).

As for the type/uni-type discussion, I don't want to get super deep into it, but for one, { ... } is a literal, it's just syntactic sugar. That's like saying that Java doesn't have a string type, because it has string literals and string constructors.

Anyway, I'm not sure how useful the definition in the article you linked is, in the discussion we were having, it's apparently a semantics debate, which, ugh, I don't want to get into. The point is, if JavaScript doesn't have types, then TypeScript doesn't check types, which I'm fine conceding. Replace every instance of the word type with contracts and same discussion. TypeScript checks contracts of prototypes of objects at compile time, which you can also check in JavaScript at run-time. It also checks structure of objects, which again, you can also check at run-time. Both of these are guaranteed.

So basically, whatever TypeScript checks at compile-time, whatever you want to call it, is also checkable at runtime. This means that there's no erasure of that "whatever" either, as you claim.

yortus commented 7 years ago

@RyanCavanaugh wrote:

Emitted TypeScript code never changes based on the types of the expressions being transpiled

Unfortunately TypeScript does have an example of this. I have raised issues about this before (#6007 and elsewhere), but so far this exception remains. You can do this in the latest nightly:

// @target: ES5
declare class Promise<T> { then(cb: (val) => any); }
declare class MyClass extends Promise<any> { }
const foo = async () => 42;
const bar = async (): MyClass => 42;

which emits:

/*...helpers omitted for brevity...*/
var foo = function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
    return [2 /*return*/, 42];
}); }); };
var bar = function () { return __awaiter(_this, void 0, MyClass, function () { return __generator(this, function (_a) {
    return [2 /*return*/, 42];
}); }); };

Note that foo and bar are identical apart from a single type annotation, but the different type annotation results in a different emit. In particular the type annotation MyClass has resulted in the MyClass constructor function appearing in the emitted code in a value position.

[EDIT: removed quotes from @spion and @SimonMeskens that refered to something else]

yortus commented 7 years ago

Since this issue also reflects on TypeScript's structural typing, I'd like add to my last comment another interesting consequence of the unique way async function typing works. When targeting ES6, the return type annotation of an async function is nominally, not structurally typed. That is unlike anything else in the language (i.e. function decls/exprs, arrow declr/exprs and generator functions are all structurally typed).

For example:

// @target: ES6

// Both functions are identical in type and runtime behaviour.
function f1() { return Promise.resolve(42); }   // OK: f1 is () => Promise<number>
async function f2() { return 42 };              // OK: f2 is () => Promise<number>

// Same functions, now with return type annotations.
// For the ordinary function f3, the return type annotation is structurally typed.
// For the async function f4, the return type annotation is nominally typed.
function f3(): {then} { return Promise.resolve(42); }  // OK: f3 is () => {then: any}
async function f4(): {then} { return 42 };             // ERROR: annotation must be Promise<T>

In ES6 an async function's return type annotation must refer nomimally to the global Promise<T> type, or be omitted altogether. The inability to use structural typing here does impose some limitations on the type system, such as described here.

SimonMeskens commented 7 years ago

Yortus, here's the original quote:

Even casting the class or interface type can change the emitted code in terms of which methods may be called

Your example is indeed weird, but not a cast.

yortus commented 7 years ago

@SimonMeskens it is a counterexample of @RyanCavanaugh's quote. EDIT: I'll remove your and @spion's quotes in my comment for accuracy sake.

SimonMeskens commented 7 years ago

You quoted @spion and I, we both were not talking about @RyanCavanaugh's quote.

yortus commented 7 years ago

@SimonMeskens fixed thanks

shelby3 commented 7 years ago

A is superset of B means that any B is valid A, but not every A is valid B.

Agreed, but I am saying that I fail to see the utility. I think if it were to have any utility instead we want a supertype aka subset, where every TypeScript would run uncompiled on every JavaScript otherwise I don't really see what utility it provides to have that attribute. The moment I avail of any feature in the superset which is not in the subset of JavaScript, my code no longer runs on JavaScript without compilation by TypeScript. As I wrote in my prior comment, I understand supersetting might ease transition since once could start from JS and add only the superset feature they wish to use. I have never argued against having a tight correspondence to JS, but nevertheless we do have to add features to TS else it is a useless endeavor. Have I ever advocated removing the superset capability which types JS as any? No! I only advocated adding features to TS.

I dare you

I need to sleep sometimes. Patience.

As for the type/uni-type discussion, I don't want to get super deep into it, but for one, { ... } is a literal, it's just syntactic sugar. That's like saying that Java doesn't have a string type, because it has string literals and string constructors.

No the point is it doesn't just have a nominal type undifferentiated from Object.prototype unless you consider the properties in the object to not be part of its type. Since I include the name and type of properties declared (but not their values), I say the type is structural and not covered by your original point about a delegation type.

You've not acknowledged my points:

it [TypeScript] offers compile time type safety

Incorrect and incorrect.

The idea is that the compiler promises you that a function is correctly called, not that it can't be incorrectly called.

Nope. It can't promise the invocation is correct at the time it is called.

Of course JavaScript is typed, it just isn't type checked.

It is uni-typed at compile-time. The runtime types may or may not be checked.

The point is that the types can change at run-time and thus there is no typing (only a uni-type of any) which is safe at compile-time.

Without the ability to reason about times at compile-time, the dynamic typing is less useful. What do I do when my code expect a type T and instead is dealing with a type Q. Throwing run-time exceptions is the antithesis of safe code, because there is no way to test all possible code paths at run-time due to the Halting Problem.

Replace every instance of the word type with prototype and same discussion.

That doesn't cover structural types. And it doesn't cover the fact that my types may include objects which reference objects and thus looking at one small fragment of my mental type may be aliasing error, i.e. encapsulation isn't enforced.

TypeScript checks contracts of prototypes of objects at compile time

It can't because it doesn't enforce that the types don't change. This is just heuristics akin to sticking our finger in the air and guessing which way the stock market will go today.

As the Complexity of Whoops (compile-time contracts broken) increases, the utility of that heuristic becomes closer to useless and even can become harmful as it is making contracts which are so frequently broken.

shelby3 commented 7 years ago

@spion

Even casting the class or interface type can change the emitted code in terms of which methods may be called. It is not true that typing in TypeScript doesn't change the code that is emitted or allowed.

Please go ahead and provide an example :)

First let's establish that TypeScript does emit code from typing judgements which is not verbatim the same code that was input to the compiler with types elided.

Playground:

abstract class Foo {}
abstract class Bar extends Foo {}
class Baz extends Bar {}

Emits:

var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Foo = (function () {
    function Foo() {
    }
    return Foo;
}());
var Bar = (function (_super) {
    __extends(Bar, _super);
    function Bar() {
        _super.apply(this, arguments);
    }
    return Bar;
}(Foo));
var Baz = (function (_super) {
    __extends(Baz, _super);
    function Baz() {
        _super.apply(this, arguments);
    }
    return Baz;
}(Bar));

When I cast a reference from one type to another, it changes at compile-time which methods I am allowed to access on that reference. Although the properties I do access is still verbatim in the source code, the typing constraint has dictated which code is allowed.

My point is I don't see the point nor consistency of claiming that TypeScript never emits inserted code from typing information, nor claiming that typing casts don't control which code is allowed.

Typeclasses require emitting some code for the declarations and then choosing which are legal at the "cast" of the function use site.

I think the distinctions you are making are arbitrary (pigeonholed) in a holistic sense, and thus I don't see what is the utility of your distinctions.

In any case, I am not interested in arguing about what the philosophy of TypeScript is. You all have stated you won't do typeclasses, and it is a sufficient answer. I don't like to waste my time arguing about subjective preferences.

If anyone cares to clearly explain the utility and objectiveness of TypeScript's design philosophy, I think that might help readers and perhaps even myself.

SimonMeskens commented 7 years ago

That's backporting emit, that doesn't count, you should show us what it does with an ES2015 target instead.

shelby3 commented 7 years ago

@SimonMeskens wrote:

That's backporting emit, that doesn't count, you should show us what it does with an ES2015 target instead.

You moved the goalposts to some arbitrary case, which is aliasing error (point sampling a different point depending on which argument you want to make). Is there only one version of JavaScript in the wild? I will repeat:

@shelby3 wrote:

I think the distinctions you are making are arbitrary (pigeonholed) in a holistic sense, and thus I don't see what is the utility of your distinctions.

And I will repeat some holistic justification might be helpful to some:

If anyone cares to clearly explain the utility and objectiveness of TypeScript's design philosophy, I think that might help readers and perhaps even myself.

In short, WTF is the point ???

Apologies for questioning what is the rationale for the orderly orderliness which is unsound and doesn't provide reliable contracts. Hopefully you can discern that I am trying to leave now as soon as you let me.

Edit: I suppose the goal is to have a trajectory that aims to look like how one would typically code in a future version of ECMAScript, except with typing hints. But for example, instead of checking the structural type to remain consistent, the community apparently decided to just trust a human to instruct (and thus potentially fool) the typing system as to what a type should be, breaking the internal consistency of the typing system while also not emitting code that checks the structural type; and checking the structural type in the absence of a nominal Symbol tag with instanceof is what I would expect to be a more idiomatic JavaScript design pattern. I don't know what to call this sort of typing system. Apparently many adhoc vectors. I won't pretend I can wrap my mind around all the corner cases of this typing system when all its features are mixed into code. Am I missing the point? What point?

RyanCavanaugh commented 7 years ago

async / await is indeed the only place where type-directed emit occurs. This is only considered OK because a) this is a huge value add, perhaps the highest emit value prop in the language and b) we can be sure that the async types required do not require loading other user-code files. Nothing else has met that bar and probably nothing else will.

RyanCavanaugh commented 7 years ago

This thread has gotten too long for anyone to read in a reasonable length of time, and the proposal under discussion has shifted substantially. It seems like we've elucidated some key constraints in TypeScript and if anyone wants to take this idea to a place where we could actually emit it, that'd be welcomed in a new issue. Closing this one (note that closing does not prevent new comments and people are welcomed to continue discussion here if needed in order to reach proper clarity for a new proposal).

SimonMeskens commented 7 years ago

Yeah, I'd gather up a proposal from this thread, but I don't see how type classes would work without polymorphic functions. Maybe we can come back to this if we ever get extension methods, maybe type classes are the way to go about extension methods. Not sure.

shelby3 commented 7 years ago

You may find interesting or useful this summary of variance issues w.r.t. to subclassing, typeclass objects, and my new union solution to the Expression Problem.

dead-claudia commented 7 years ago

@shelby3 I'd like to clarify that the private fields proposal is theoretically transpilable (with acceptable performance) to ES6, but it can only be accomplished with WeakMaps, and thus can't go down to ES5.1 without native weak bindings (like node-weak in Node or java.util.WeakHashMap in Rhino or Nashorn).

On Sat, Sep 17, 2016, 22:12 shelby3 notifications@github.com wrote:

@SimonMeskens https://github.com/SimonMeskens wrote:

you're showing the world

Hyperbole.

You're not holding a mirror up

I am also not blind in both eyes.

Any objects that share the same delegation object are of the same type

{ ... } undifferentiated from Object.prototype is uni-typing. As I wrote, "sometimes..."

As for TypeScript not being a superset, how is it not?

I have a different definition of superset. I can't take any TypeScript code and run it on JavaScript without compiling. It is a useless point that TypeScript can compile JavaScript.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Microsoft/TypeScript/issues/10844#issuecomment-247819798, or mute the thread https://github.com/notifications/unsubscribe-auth/AERrBMHpTM_QRM5eYy-ahJHQHQzTbiK0ks5qrJ4mgaJpZM4J52xn .

kitsonk commented 7 years ago

without native weak bindings

Note that WeakMap can be fully polyfilled in ES5. I still think that the proposed private fields could be fully down emitted to ES5.

dead-claudia commented 7 years ago

@kitsonk I was mainly referring to the memory characteristics. (Using a pair of backing arrays is technically spec conforming, although it's highly memory inefficientc.) And the existing userland "polyfill" that is mostly weak is not truly weak IIRC.

On Fri, Sep 23, 2016, 05:19 Kitson Kelly notifications@github.com wrote:

without native weak bindings

Note that WeakMap can be fully polyfilled in ES5. I still think that the proposed private fields could be fully down emitted to ES5.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Microsoft/TypeScript/issues/10844#issuecomment-249143560, or mute the thread https://github.com/notifications/unsubscribe-auth/AERrBBA-Qo8degAhkqoVADdw896wcV4dks5qs5mGgaJpZM4J52xn .

kitsonk commented 7 years ago

And the existing userland "polyfill" that is mostly weak is not truly weak IIRC.

I don't believe the core-js polyfill is leaky (though there maybe a few edge cases... I know there were some changes to deal with frozen/sealed keys which would leak). Actually one of the changes of the spec, before it was final (removal of WeakMap.prototype.clear()) was specifically to make it possible to make it polyfillable with similar GC features to native.

dead-claudia commented 7 years ago

@kitsonk You're right. I was wrong. (I had to read the source.)

So apparently, the ES private fields proposal is transpilable down to ES5, complete with weak semantics, just using a WeakMap polyfill if necessary.

(I'm the one who pointed out in the proposal's repo before it was migrated to TC39 that its semantics could actually be transpiled to ES6 using WeakMaps, which is why I commented in the first place. The proposed spec change itself uses WeakMaps to specify it, although engines really shouldn't actually use those to implement it.)

shelby3 commented 7 years ago

The original idea of this thread, becomes the fast track hack to get typeclasses into TypeScript via my proposal to have ZenScript transpile to TypeScript. Thoughts?

masaeedu commented 7 years ago

With much respect to all involved, this thread is far too long and contains far too many edits and digressions to be able to parse out a proposal for type classes. @shelby3 or someone else who is the primary proponent of this, would you be so kind as to post a new issue with a concrete proposal distilled from this discussion?