hhvm / xhp-lib

Class libraries for XHP. XHP is a Hack feature that augments the syntax of the language such that XML document fragments become valid Hack expressions.
http://facebook.github.io/xhp-lib/
MIT License
1.38k stars 160 forks source link

replace `children` declaration language feature with a trait without any special syntax #212

Closed fredemmott closed 4 years ago

fredemmott commented 4 years ago

My perspective

It feels like it's likely to stay in limbo.

With the introduction of reified generics, I think it can actually be implemented better as a standard Hack trait, without any special syntax.

Why is it weird?

Proposal

  1. prototype the rough idea below in open source XHP-Lib, HHAST, and migrate docs.hhvm.com
  2. log when trait-based and existing validation differs.
  3. FB ports to FB www
  4. delete the syntax from the parser

Rough idea:

class :foo extends :x:element {
  use XHPChildrenValidation;

  // old:
  //  children (:bar*, :baz?, pcdata);

  // new:
  protected static function getChildValidationPattern(): XHPChildPattern {
    return XHPChild::tuple(
      XHPChild::anyNumberOf(XHPChild::ofType<:bar>()),
      XHPChild::optional(XHPChild::ofType<:baz>())
      XHPChild::pcdata(),
    );
  }

  protected async function genRender() {
    $this->validateChildren(); // or, automatic:
    // - maybe check for presence of `IHaveChildValidation`
    //   or similar in framework?
    // - maybe make trait override getChildren() and friends
  }
}

XHPChild::anyNumberOf(XHPChild::ofType<:bar>()) is a bit verbose; the problem with XHPChild::anyNumberOf<:bar>() or similar is patterns like children ((:bar | :baz)*) - with the verbose syntax, they're something like XHPChild::anyNumberOf(XHPChild::ofUnionType(XHPChild::ofType<:bar>(), XHPChild::ofType<:baz>()) , but I don't see a clean way to fit them in to something like XHPChild::anyNumberOf<T>()

Migrations

This can be done automatically:

Verifying correctness

Why do reified generics help?

They allow XHPChild::of<T>() consistently, rather than XHPChild::string(), XHPChild::instanceOfClass(classname<T>) etc

Additionally, classname<T> is getting harder to use, as the typechecker gets stricter about unsafe usage.

Why is it better?

How to start?

Wilfred commented 4 years ago

I think this is a very worthwhile change.

(1) Hack is a big language and removing this would make learning the language easier. The syntax here is really weird and not used elsewhere (e.g. %my-category, :my-class+).

(2) There's no type checker support today, and we don't have any plans to implement the full attribute/children patterns in the type checker.

(3) Since the checking of children happens in userland code at runtime anyway, it would be more consistent to use normal code to define the constraints.

(Since XHP is very old and reified generics are very new, I'm sure there will be a few bugs when we initially exercise it FWIW.)

fredemmott commented 4 years ago

Today's hack nightlies add disable_xhp_children_declarations hhconfig option

fredemmott commented 4 years ago

This is mostly done - though xhp-lib should be modified to throw an exception if an old-style children declaration is present.