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

Statics subtyping (RFC: Newable<.> to indicate constructor subtyping) #1847

Closed
wants to merge 9 commits into from

Conversation

popham
Copy link
Contributor

@popham popham commented May 25, 2016

RFC:
I feel that the sticky point here is the lack of subtyping on class constructors. Suppose class B extends class A, but B implements a constructor that doesn't subtype that of A.

var K: Class<A> = B; //OK under this PR, NG under master
var a = new K(...);

Working to the signature of A within ... implies a false positive, while working to the signature of B within ... implies a false negative. This same problem shows up in class methods that new this.constructor and static class methods that new this. I think that this is easily handled for implicit annotations with a newable flag on insttype, but for explicit annotations the annotation's syntax would probably be ugly.

  • In the example above, newable=true is necessary for 90% of use cases. (Hence the unification rule within ClassT~>ClassT?)
  • In class methods with an a: A, newable=false is sufficiently strong in 99% of use cases (1% generously for new a.constructor).
  • In general for a: A, newable=false is sufficiently strong in 99% of use cases.
  • In class methods with a K: Class<A>, newable=false is sufficiently strong in 50% of use cases(?).
  • (My thinking here is biased toward a program that has been explicitly annotated in its entirety. Do these numbers shift profoundly in minimally annotated programs?)

Does there currently exist a plan and/or demand to tighten things up here? My quick and dirty thinking is the newable flag on insttype and a Newable<.> wrapper for explicit annotations that impose constructor subtyping in addition to the norm:

var K: Class<Newable<A>> = B; //NG: constructor subtyping fails
var a = new K(...);

I think that this is preferable to some pretty-sounding variation of NonNewable because of the prevalence of the a: A use case.

(And currently, Class<.> annotations are kinda useless, aren't they? If the annotation necessarily contains the class type, then why shuffle it around? Why provide it as a parameter? I thought ES6 was spec'd to avoid circular importing problems, eliminating that possibility(?). I suspect that post-transpile, broken circular-imports may be the only credible use case.)

@popham
Copy link
Contributor Author

popham commented May 28, 2016

Currently chasing: Previously chased:

interface I {
  anotherMethod(): number;
}

interface J {
  static anotherStaticMethod(): number;
}

class MS {
  anotherMethod(): number {
    return 3;
  }
  static anotherStaticMethod(): number {
    return 4;
  }
}

class A {
  static sm3(): Class<I> & Class<J> {
    return MS;
  }
}

var Y = A.sm3();
var y = new Y();
var n6: number = y.constructor.anotherStaticMethod(); 

n6 should be okay but I get

 28: var n6: number = y.constructor.anotherStaticMethod();
                                    ^^^^^^^^^^^^^^^^^^^ property `anotherStaticMethod`. Property not found in
 28: var n6: number = y.constructor.anotherStaticMethod();
                      ^ statics of I

ConstructorT doesn't distribute across intersection members. If there exists more than one constructor amongst the interfaces, I guess you can take your pick looking for a successful one (Nope--all of the ctors should be compatible.) I suspect that this aligns with ordinary methods on interfaces. Regardless, [T]he result of the new should be an IntersectionT still. My [wrong] opinion for a while has been that constructors shouldn't be allowed on interfaces. --interfaces should be entirely subtyped. However, if the intersection contains a class, then this case still arises.

@popham
Copy link
Contributor Author

popham commented May 29, 2016

I thought interfaces should forbid constructors, but I want a particular use case:

class A{}
interface Extensions {}
function extender(): Class<A> & Class<Extensions> {
  return class E extends A {
    // satisfy `Extensions` interface
  }
}

Classes created by extender should export fine, where each has its class id erased, but is still pretty tightly typed. The problem arises, however, what if I want Extensions to impose the constructor? Interfaces need constructors :(. Tests for this PR should use identical constructor signatures to dance around this.

@popham popham force-pushed the statics-subtyping branch 2 times, most recently from 666d44f to 383c818 Compare May 30, 2016 00:20
@popham popham changed the title Statics subtyping Statics subtyping (RFC: newable-flag on insttype to indicate constructor subtyping) Jun 4, 2016
@@ -1848,6 +1848,24 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
(List.hd ts,
LookupT (reason, strict, (List.tl ts) @ try_ts_on_failure, s, t))

| IntersectionT (r, rep), GetPropT (reason_op, (reason_prop, "constructor"), t) ->
Copy link
Contributor Author

@popham popham Jun 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generalize with an is_set_op_distributive gate on u, and handle UnionT too.

@popham popham changed the title Statics subtyping (RFC: newable-flag on insttype to indicate constructor subtyping) Statics subtyping (RFC: Newable<.> to indicate constructor subtyping) Jun 7, 2016
@ghost ghost added the CLA Signed label Jul 12, 2016
@gabelevi
Copy link
Contributor

@popham - I'm making an effort to clean out our oldest pull requests. What's the status of this PR? Are you waiting for feedback? Planning some more changes? Or is this abandoned?

By default, I'm closing out old PRs that I can't merge. This does not reflect the quality of the PR. Please feel free to re-open this if you think it should still be open.

@gabelevi gabelevi closed this Oct 24, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants