user-documentation icon indicating copy to clipboard operation
user-documentation copied to clipboard

Clarify trait semantics

Open Wilfred opened this issue 4 years ago • 7 comments

  • you can define a constructor in a trait

  • trait T implements IFoo and trait T { require implements IFoo; } are both legal, but have slightly different meanings

  • You can call T::someStaticMethod() directly today (but this should hopefully be banned soon)

  • You can define abstract methods in traits

  • You can use __ConsistentConstruct on a trait, and it's different to a class

Wilfred avatar Jul 09 '20 18:07 Wilfred

  • private methods in traits are only copied into using classes if the class doesn't have one. effectively this means a class can override a trait private method.
  • every class using a trait gets its own copy of the trait's static properties
  • but only one copy, if the trait is inherited more than once: trait T1{} trait T2{use T1;} trait T3{use T1}; class C{use T2,T3};
  • in class C{use T1,T2}, if T1+T2 define conflicting abstract methods, T1 wins... but if either T1 or T2 has a concrete method, that one wins. finally, if C has the method as well, C wins.
  • in the same example, if T1+T2 define conflicting concrete methods, it's an error unless C has the method.

but have slightly different meanings

what are the two meanings?

T::someStaticMethod()

what happens if someStaticMethod() contains language constructs (e.g. parent or access to static properties) that only work in the context of a using class?

edwinsmith avatar Jul 09 '20 18:07 edwinsmith

private methods in traits are only copied into using classes if the class doesn't have one.

HH typechecks this with override semantics - C's concrete method must have a compatible signature with the private method in T. hhvm doesn't care (T's private method is simply hidden).

edwinsmith avatar Jul 09 '20 18:07 edwinsmith

__CLASS__ in a trait is rewritten to the class using the trait.

edwinsmith avatar Jul 10 '20 13:07 edwinsmith

T::somestaticMethod()

what happens if someStaticMethod() contains language constructs (e.g. parent or access to static properties) that only work in the context of a using class?

It throws an exception/fatals. This is why we need to ban it :)

Wilfred avatar Jul 10 '20 18:07 Wilfred

what are the two meanings?

Given two traits:

interface IHasFoo {
  public function foo(): void;
}

trait FooWithRequire {
  require implements IHasFoo;
}

trait FooWithImplements implements IHasFoo {}

Using them:

class ExampleOne implements IHasFoo {
  use FooWithRequire;
  public function foo(): void {}
}

class ExampleTwo {
  use FooWithImplements;
  public function foo(): void {}
}

ExampleOne is required to write implements IHasFoo, whereas ExampleTwo implicitly gets implements IHasFoo from using the trait.

https://fburl.com/1810mh57 has some additional discussion.

Wilfred avatar Jul 10 '20 18:07 Wilfred

IIUC, both ExampleOne and ExampleTwo end up identical -- they both have the same bases, interfaces, and methods, correct? That said, I agree there is value to only allowing ExampleOne style.

edwinsmith avatar Jul 11 '20 20:07 edwinsmith

both ExampleOne and ExampleTwo end up identical

Yep, the only difference is that you're required to write implements IHasFoo for ExampleOne. At runtime they should be the same.

Wilfred avatar Jul 13 '20 18:07 Wilfred