You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
These strategies are based on XHP being practically unusable in combination with namespace; assumptions are:
all code that uses XHP is in the root namespace
all code that defines XHP is in the root namespace
Required
temporarily change XHPClassNameToken to mangle differently, so that :foo:bar and \foo\bar (or class \foo\xhp_bar, depending on proposal) are considered equivalent
temporarily change allow class :foo:bar to define class \foo\bar {} (or class \foo\xhp_bar)
this does not affect resolution of names within the definition (example below)
once that's been supported for $n (>=1) releases, migrate with hackast/hhast, and remove old behavior
Resolution example
// TEMPORARY: this defines class \foo\bar or class \foo\xhp_barclass :foo:bar {
attributeMyAttrfoo; // this references \MyAttr - *NOT* \foo\MyAttr
}
This is likely straightforward to implement, as explicitly declaring class Foo\Bar {} was accidentally supported in the
initial release of HackC until explicitly banned.
Optional
Provide a flag to entirely preserve the existing "only works in root namespace" behavior and mangling. Deployments
with this flag enabled would be unable to combine XHP with namespaces.
Never delete the flag to allow class :foo:bar -> class foo\bar {} (or class foo\xhp_bar {} mangling
ban this syntax outside of the root namespace
removes need for mass migration of existing code, but allows use of namespaced XHP for new use cases
not completely transparent: error messages would report class foo\bar instead of foo:bar
alternatively, when rendering class names in error messages, render foo:bar if the class is an XHP class; this trades
consistency for existing code against consistency for new namespace-using XHP code (e.g. namespace MyFeature { class :foo {} } would be reported as :MyFeature:foo
It is currently impossible to use XHP with namespaces:
class :foo extends \:x:element{} is a parse error
<\foo:bar> is a parse error (and confusing with </foo:bar>
<\foo\bar>baz</\foo\bar> is the obvious way, but </\foo\bar> and <\foo\bar /> are unreadable and annoying to type. Aim for : to be used as a replacement for \ in XHP contexts.
Remaining problem
How should use type and similar work for XHP? I see this as two strongly coupled questions:
how should XHP names be mangled?
what namespace separator should be used in what contexts? Contexts include:
type specifier: function foo(:bar:baz $xhp) (current)
xhp constructor calls: <bar:baz> (current)
imports: use type :bar:baz; use namespace :foo; use namespace :foo:bar; (proposal 1)
With an example:
namespace MyNS {
class MyClass {}
class :myxhpclass {}
}
namespace {
use type MyNS\MyClass;
// and what for :myxhpclass?
use type MyNS\{MyClass, /* ? */ };
}
Current situation
:foo:bar-baz::class is xhp_foo__bar_baz
no namespace separator works correctly with XHP
Proposal 1: : always for XHP, \ always for non-XHP
:foo:bar-baz::class is foo\xhp_bar_baz
anything relating to XHP uses :; this keeps XHP class names seeming like they have another symbol table (i.e. class :foo doesn't conflict with class foo)
type specifier: function foo(:bar:baz $xhp)
XHP constructor calls: <bar:baz> if use namespace bar is present or bar is relative to the current namespace, <:bar:baz> for relative to the root namespace. </:bar:baz> to close.
imports: use type :bar:baz;, `use namespace :bar:baz;
namespace foo {
class MyClass {}
class :MyXHPClass {}
}
namespace foo\bar {
class MyOtherClass;
class :MyOtherXHPClass {}
}
namespace {
use type foo\MyXHPClass;
$_ = <MyXHPClass />; // tries to instantiate \xhp_bar_baz; not affected by non-XHP `use type`
use type :foo:MyXHPClass;
$_ = <MyXHPClass />; // \foo\xhp_MyXHPClass
use type foo\{MyClass, MyXHPClass}; // second class is not defined.
use namespace foo\bar;
new bar\MyOtherClass(); // works
$_ = <bar:MyOtherXHPClass />; // unaffected by use ns; this is \bar\xhp_MyOtherXHPClass;
use namespace :foo:bar;
<bar:MyOtherXHPClass>; // \foo\bar\xhp_MyOtherXHPClass
// fully-qualified
$_ = <:foo:MyXHPClass />;
$_ = <:foo:bar:MyOtherXHPClass />;
// Problem?
use type :foo:MyXHPClass;
function takes_foo_MyXHPClass(:MyXHPClass $_); // ?
function takes_root_MyXCHPClass(::MyXHPClass $_); // ?
}
Notes
through mangling, effectively keeps XHP and non-XHP classes in non-colliding name tables (unless you call non-XHP classes xhp_foo...)
using the same symbol to specify 'this is XHP class' and to qualify names leads to confusing cases like the last few lines of the example
Proposal 2: interchangeable always
Allow : in all contexts where \ is a namespace separator.
:foo:bar-baz::class is \foo\bar_baz
anything relating to XHP uses :; this keeps XHP class names seeming like they have another symbol table (i.e. class :foo doesn't conflict with class foo)
type specifier: function foo(:bar:baz $xhp) or function foo(\bar\baz $xhp) or function foo(:bar\baz $xhp)
XHP constructor calls: <bar:baz> or <bar\baz
imports: use type :bar:baz;, use namespace :bar:baz;, use type \bar\baz etc.
Example
use type :foo:bar;
new bar(); // creates a new \foo\bar
$_ = <bar />; // also creates a new \foo\bar
// no problem:
use type :foo:MyXHPClass;
function takes_foo_MyXHPClass(MyXHPClass $_);
function takes_root_MyXCHPClass(\MyXHPClass $_);
function takes_root_MyXCHPClass(:MyXHPClass $_); // same thing
Notes
completely unified XHP and non-XHP namespaces
class :foo {} and class foo {} define a class with the same name and collide
requires - to be permitted in type names (though may be banned in non-XHP decls)
return types are slightly weird: function foo(): :bar:baz; this already exists, but could become more prevalent if not limited to returning XHP, which is rare
ternary operators also weird
unknowns: other uses of : token
allows users to decide (3) or (4) ... or whatever else they want and enforce with linters
Proposal 3: \ always, except for : in XHP constructor calls
:foo:bar-baz::class is invalid as :foo:bar-baz is not a type. an XHP class bar-baz in the foo namespace is foo\bar_baz.
XHP constructor calls use :; all other contexts use \.
type specifier: function foo(\bar\baz $xhp)
XHP constructor calls: <bar:baz> if use namespace bar is present or bar is relative to the current namespace, <:bar:baz> for relative to the root namespace.
imports: use type \bar\baz;, use namespace bar\baz;
completely unifies XHP and non-XHP Hack namespaces
class :foo {} and class foo {} define a class with the same name and collide
requires - to be permitted in type names (though may be banned in non-XHP decls)
Proposal 4: \ in XHP
:foo:bar-baz::class is invalid as :foo:bar-baz is not a type. an XHP class bar-baz in the foo namespace is \foo\bar_baz.
\ everyehere:
type specifier: function foo(\bar\baz $xhp)
XHP constructor calls: <bar\baz> if use namespace bar is present or bar is relative to the current namespace, <\bar\baz> for relative to the root namespace.
imports: use type \bar\baz;, use namespace bar\baz;
Notes
largely equivalent to (3):
only difference is the token used in XHP constructor calls is not dicated
(4) is more consistent, but has more 'eww'
the main argument against this is "eww </\foo\bar> and <\foo\bar /> are icky". That said, everyone strongly agrees with that :p
completely unifies XHP and non-XHP Hack namespaces
class :foo {} and class foo {} define a class with the same name and collide
requires - to be permitted in type names (though may be banned in non-XHP decls)
Related question: replace class :foo with xhp class foo
Every proposal except for (1) require that a normal class called foo and an xhp class called foo in the same namespace collide - i.e.
they require that use type foo\bar; works regardless of if bar if an XHP or non-XHP class.
It is likely to be confusing that class :foo and class foo collide; this is much clearer for xhp class foo {} and class foo {}.
Some marker is needed at the declaration, for example:
to autogenerate the constructor
to permit the use of attribute and children declarations
to permit the use of - in type names (or mangle it to _ like we currently do)
Suggested path
xhp class foo {}
proposal 3: : for XHP constructor calls, \ everywhere else.
these can/should be independently landed changes, but should aim for the period of time when we have one without the other to be as small as possible
Rationale
xhp class rationale covered in previous section
(4) (\ everywhere) is largely equivalent to (3) (: at XHP constructor calls, \ everywhere else), but less readable and with more churn needed. Drop.
(1) has ambiguities in type names. Drop.
This leaves (2) (interchangeable tokens) and (3). Go for 3 as:
we generally prefer having one way to do things; if there are multiple, we'd still need to decide on one approach
to use consistently in FB codebases and docs.hhvm.com. Likely that whatever we use there will be the de-facto standard.