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

add object type #8461

Closed
azjezz opened this issue Mar 9, 2019 · 5 comments
Closed

add object type #8461

azjezz opened this issue Mar 9, 2019 · 5 comments

Comments

@azjezz
Copy link
Contributor

azjezz commented Mar 9, 2019

Hack have a great type system, However it is not currently possible to declare that a function either needs to be passed an object as a parameter, or to declare that a function must return an object.

this feature request proposes that object should be used as a parameter type and as a return type. Any object would pass the type check.

Passing a value that is not an object to a parameter that is declared as type object would fail the type check, and a TypeError would be thrown.

Similarly, if a function is declared as returning an object but does not return an object, again a TypeError would be thrown.

As it would be used internally, object would become a reserved classname, unavailable for use as a class name in userland code.

Examples

src/Objects/classname.hack

namespace Toolkit\Objects;

function get_classname<T as object>(T $object): classname<T> {
  // UNSAFE ( php stdlib )
  return \get_class($object);
}

main.hack

// include autoloader...

use function Toolkit\Objects\get_classname;

<<__EntryPoint>>
function main(): void {
  $a = get_classname(new stdClass()); // classname<\stdClass>
  $b = get_classname(Vector { }); // classname<\HH\Vector>
  // This can be statically analysed to contain an error.
  // and would throw a `TypeError` Exception at runtime.
  $c = get_classname('foo');
}

object is not generic

if object is generics, it would be pointless, as this code :

function foo<T as BarInterface>(object<T> $bar): void {}

is same as :

function foo<T as BarInterface>(T $bar): void {}

meaning, there's no use case for a generic object type.

covariance/contravariance

interface Factory {
  public function create(): object;
}

class Foo {}

class FooFactory implements Factory {
  // permitted, `Foo` is in fact an object
  public function create(): Foo {
    return new Foo();
  }
}

class SessionIdFactory implements Factory {
  // Not permitted, `string` is not an object.
  public function create(): string {
    return 'SESSION_ID';
  }
}
@fredemmott
Copy link
Contributor

I remember us explicitly not wanting this, but don't remember exactly why... maybe @Wilfred ?

@azjezz azjezz changed the title [Hack][Feature Request] add object type add object type Mar 11, 2019
@Wilfred
Copy link
Contributor

Wilfred commented Mar 12, 2019

The situation today: The type checker does have a base type for objects (Tobject) but it's not exposed to the user. Also, I believe there's no object base class at runtime (you don't have to inherit from stdClass either).

I chatted about this briefly with @manzyuk and @andrewjkennedy. I think the view was that there aren't many use cases, and Andrew suggested that you often really want existential types.

I'm not aware of any reason we'd explicitly not want this though. It seems fairly innocuous to me.

@azjezz
Copy link
Contributor Author

azjezz commented Mar 12, 2019

this would be really helpful to limit generics to only object
example, i want all Factory<T> implementations to return objects, but currently i can't do this.

with object :

interface ServiceContainer {
  public function has<T as object>(classname<T> $service): bool;
  public function get<T as object>(classname<T> $service): T;
}

interface Factory<T as object> {
  public function create(ServiceContainer $container): T;
}

// okay
interface SessionFactory implements Factory<Session> {
  ...
}

// noop
interface IdFactory implements Factory<string> {
  ...
}

the work around for now would be :

interface Service {}

interface ServiceContainer {
  public function has<T as Service>(classname<T> $service): bool;
  public function get<T as Service>(classname<T> $service): T;
}

interface Factory<T as Service> {
  public function create(ServiceContainer $container): T;
}

// okay
interface SessionFactory implements Factory<Session> {
  ...
}

// noop
interface IdFactory implements Factory<string> {
  ...
}

but this would require all services to implement the Service interface, which is not a good idea. ( e.g : you would have to create a "wrapper" for the mysql async pool that implements Service if you want a factory for it )

@azjezz
Copy link
Contributor Author

azjezz commented May 23, 2020

another usage example is provided in my comment on #5317

@lexidor
Copy link
Collaborator

lexidor commented Jun 9, 2024

Resolved by adding HH\object.
93db8b6

@lexidor lexidor closed this as completed Jun 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants