# [RFC][Vote] Return Types

- Thread: https://externals.io/message/78608
- Original archive: https://news-web.php.net/php.internals/78608
- Started by Levi Morrison on 2014-11-04T02:01:42+00:00
- Messages: 22
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #78608 — Levi Morrison — 2014-11-04T02:01:42+00:00

URL: https://externals.io/message/78608#78608

Dear Internals,

I am pleased to announce that the return type RFC is now open for
voting[1]. Note that the implementation needs a bit of cleaning up
before merging should the RFC be accepted; Dmitry has already
volunteered to help with that, but more eyes are welcome.

A couple of things have changed in reflection[2] since the last
iteration, but is otherwise the same.

The implementation is slightly out of date with the RFC, but soon it
will be updated to match.

  [1]: https://wiki.php.net/rfc/returntypehinting#vote
  [2]: https://wiki.php.net/rfc/returntypehinting#reflection

---

## #78609 — Levi Morrison — 2014-11-04T02:16:49+00:00

URL: https://externals.io/message/78608#78609 · In reply to: #78608

> Dear Internals,
>
> I am pleased to announce that the return type RFC is now open for
> voting[1]. Note that the implementation needs a bit of cleaning up
> before merging should the RFC be accepted; Dmitry has already
> volunteered to help with that, but more eyes are welcome.
>
> A couple of things have changed in reflection[2] since the last
> iteration, but is otherwise the same.
>
> The implementation is slightly out of date with the RFC, but soon it
> will be updated to match.
>
>   [1]: https://wiki.php.net/rfc/returntypehinting#vote
>   [2]: https://wiki.php.net/rfc/returntypehinting#reflection

I failed to note that voting will be open until the evening of
November 14th, UTC-7.

---

## #78613 — Ivan Enderlin @ Hoa — 2014-11-04T08:06:45+00:00

URL: https://externals.io/message/78608#78613 · In reply to: #78608

Le 04/11/2014 03:01, Levi Morrison a écrit :
> Dear Internals,
>
> I am pleased to announce that the return type RFC is now open for
> voting[1]. Note that the implementation needs a bit of cleaning up
> before merging should the RFC be accepted; Dmitry has already
> volunteered to help with that, but more eyes are welcome.
>
> A couple of things have changed in reflection[2] since the last
> iteration, but is otherwise the same.
>
> The implementation is slightly out of date with the RFC, but soon it
> will be updated to match.
>
>    [1]: https://wiki.php.net/rfc/returntypehinting#vote
>    [2]: https://wiki.php.net/rfc/returntypehinting#reflection
Excellent RFC, thank you. However, there is no mention about using 
`static`, `self` or `parent` as a type (it is possible for an argument). 
Something like:
```php
class Singleton {

     public function getInstance ( $x, $y): static { … }
}
```
Maybe I miss it in the discussion?
Thoughts?

Regards.

-- 
Ivan Enderlin
Developer of Hoa
http://hoa-project.net/

PhD. at DISC/Femto-ST (Vesontio) and INRIA (Cassis)
http://disc.univ-fcomte.fr/ and http://www.inria.fr/

Member of HTML and WebApps Working Group of W3C
http://w3.org/

---

## #78615 — Christoph Becker — 2014-11-04T08:37:41+00:00

URL: https://externals.io/message/78608#78615 · In reply to: #78613

Ivan Enderlin @ Hoa wrote:

> Excellent RFC, thank you. However, there is no mention about using
> `static`, `self` or `parent` as a type (it is possible for an argument).
> Something like:
> ```php
> class Singleton {
> 
>     public function getInstance ( $x, $y): static { … }
> }
> ```
> Maybe I miss it in the discussion?
> Thoughts?

The possible usage of self and parent is already documented:
<https://wiki.php.net/rfc/returntypehinting#differences_from_past_rfcs>.

Regarding static, see <http://news.php.net/php.internals/78116>.

-- 
Christoph M. Becker

---

## #78616 — Ivan Enderlin @ Hoa — 2014-11-04T08:40:28+00:00

URL: https://externals.io/message/78608#78616 · In reply to: #78615

Le 04/11/2014 09:37, Christoph Becker a écrit :
> Ivan Enderlin @ Hoa wrote:
>
> > […]
> The possible usage of self and parent is already documented:
> <https://wiki.php.net/rfc/returntypehinting#differences_from_past_rfcs>.
>
> Regarding static, see <http://news.php.net/php.internals/78116>.
Thanks :-).

-- 
Ivan Enderlin
Developer of Hoa
http://hoa-project.net/

PhD. at DISC/Femto-ST (Vesontio) and INRIA (Cassis)
http://disc.univ-fcomte.fr/ and http://www.inria.fr/

Member of HTML and WebApps Working Group of W3C
http://w3.org/

---

## #78718 — Dmitry Stogov — 2014-11-05T11:53:52+00:00

URL: https://externals.io/message/78608#78718 · In reply to: #78608

Hi Levi,

The patch is here https://gist.github.com/dstogov/8deb8b17e41c1a5abf88

I improved memory consumption and unified return type-hinting error
behavior with existing argument type-hinting.
The main semantic must be unchanged.
I also removed part of Reflection changes. I think we should remove even
more and later introduce similar APIs for both argument and return
type-hinting at once.

Working on the patch I found few problems.

1) Return type-hinting for generators in existing state is useless. We may
use it for checking "yield" value instead of "returned" or just disable it
for now.

2) "Covariant" return type check is really problematic. It delays class
binding to run-time, and requires all classes, used in hints, to be loaded.
I would prefer to simplify this by using "Invariant" return types (similar
to arguments) and checking just class names (without their loading).

Thanks. Dmitry.


On Tue, Nov 4, 2014 at 5:01 AM, Levi Morrison <morrison.levi@gmail.com>
wrote:

> Dear Internals,
>
> I am pleased to announce that the return type RFC is now open for
> voting[1]. Note that the implementation needs a bit of cleaning up
> before merging should the RFC be accepted; Dmitry has already
> volunteered to help with that, but more eyes are welcome.
>
> A couple of things have changed in reflection[2] since the last
> iteration, but is otherwise the same.
>
> The implementation is slightly out of date with the RFC, but soon it
> will be updated to match.
>
>   [1]: https://wiki.php.net/rfc/returntypehinting#vote
>   [2]: https://wiki.php.net/rfc/returntypehinting#reflection

---

## #78719 — Andrea Faulds — 2014-11-05T12:45:01+00:00

URL: https://externals.io/message/78608#78719 · In reply to: #78718

> 
> 
> Hi Levi,
> 
> The patch is here https://gist.github.com/dstogov/8deb8b17e41c1a5abf88
> 
> I improved memory consumption and unified return type-hinting error
> behavior with existing argument type-hinting.
> The main semantic must be unchanged.

This sounds good.

> I also removed part of Reflection changes. I think we should remove even
> more and later introduce similar APIs for both argument and return
> type-hinting at once.

I'd rather not go through 3 weeks of internals to get in ReflectionType for parameters. I'd imagine adding ->getReflectionType for parameters would be fairly uncontroversial, can we just do that now?

> Working on the patch I found few problems.
> 
> 1) Return type-hinting for generators in existing state is useless. We may
> use it for checking "yield" value instead of "returned" or just disable it
> for now.

I don't think it's useless. You can require a function to be a generator. Is that bad?

If we added generics in future, we could then type the yielded type, e.g. Generator<int> maybe?


> 2) "Covariant" return type check is really problematic. It delays class
> binding to run-time, and requires all classes, used in hints, to be loaded.

The former sounds like a problem, but doesn't inheritance require these classes to be loaded anyway?

Do we always load the class in the hint? We could perhaps only do it for inheritance checks?

> I would prefer to simplify this by using "Invariant" return types (similar
> to arguments) and checking just class names (without their loading).

This is problematic, it'd stop classes from changing return types to subclasses in inheritance, which would make them less useful.
--
Andrea Faulds
http://ajf.me/

---

## #78746 — Stas Malyshev — 2014-11-05T21:05:46+00:00

URL: https://externals.io/message/78608#78746 · In reply to: #78719

Hi!


> Do we always load the class in the hint? We could perhaps only do it
> for inheritance checks?

No, classes are not loaded for type checks, since it would be pointless
- if the class is not loaded, you could not have a value of that type,
so if the class is not present, the answer is "no".

-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/

---

## #78747 — Andrea Faulds — 2014-11-05T21:24:47+00:00

URL: https://externals.io/message/78608#78747 · In reply to: #78746

> 
> 
> Hi!
> 
> 
> > […]
> 
> No, classes are not loaded for type checks, since it would be pointless
> - if the class is not loaded, you could not have a value of that type,
> so if the class is not present, the answer is "no".

I'm aware master doesn't. I'm asking about the patch. :/

--
Andrea Faulds
http://ajf.me/

---

## #78749 — Dmitry Stogov — 2014-11-05T21:43:50+00:00

URL: https://externals.io/message/78608#78749 · In reply to: #78746

On Thu, Nov 6, 2014 at 12:05 AM, Stas Malyshev <smalyshev@sugarcrm.com>
wrote:

> Hi!
>
>
> > […]
>
> No, classes are not loaded for type checks, since it would be pointless
> - if the class is not loaded, you could not have a value of that type,
> so if the class is not present, the answer is "no".
>

It's not true anymore, with this proposal.

Thanks. Dmitry.


>
> --
> Stanislav Malyshev, Software Architect
> SugarCRM: http://www.sugarcrm.com/

---

## #78751 — Andrea Faulds — 2014-11-05T22:02:10+00:00

URL: https://externals.io/message/78608#78751 · In reply to: #78749

> 
> 
> On Thu, Nov 6, 2014 at 12:05 AM, Stas Malyshev <smalyshev@sugarcrm.com>
> wrote:
> 
> > […]
> 
> It's not true anymore, with this proposal.

Thus my suggestion that we only load for the inheritance checks (and only if the type differed). We don't need to normally.

--
Andrea Faulds
http://ajf.me/

---

## #78752 — Stas Malyshev — 2014-11-05T22:15:47+00:00

URL: https://externals.io/message/78608#78752 · In reply to: #78749

Hi!

>     No, classes are not loaded for type checks, since it would be pointless
>     - if the class is not loaded, you could not have a value of that type,
>     so if the class is not present, the answer is "no".
> 
> 
> It's not true anymore, with this proposal.

This is not good. The base premise of type checks always was if you
don't use them, they are basically free - you don't load the classes,
you don't do any work, until you actually need them, and by then your
class is supposed to be already loaded, so again it's not costly. But if
just declaring a type could trigger loading of code, this may
significantly increase the footprint and loading time for classes that
have many options but only use some of them at the time.
Also, it is kind of weird that arguments require exact match but return
types do not. Not that we care for consistency anymore...
-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/

---

## #78753 — Dmitry Stogov — 2014-11-05T22:26:31+00:00

URL: https://externals.io/message/78608#78753 · In reply to: #78752

I would also prefer to use the same return type hinting compatibility rules
as for argument type-hinting.
May be it's less smart, but more practical.

http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29

Thanks. Dmitry.

On Thu, Nov 6, 2014 at 1:15 AM, Stas Malyshev <smalyshev@sugarcrm.com>
wrote:

> Hi!
>
> > […]
> pointless
> > […]
> type,
> > […]
>
> This is not good. The base premise of type checks always was if you
> don't use them, they are basically free - you don't load the classes,
> you don't do any work, until you actually need them, and by then your
> class is supposed to be already loaded, so again it's not costly. But if
> just declaring a type could trigger loading of code, this may
> significantly increase the footprint and loading time for classes that
> have many options but only use some of them at the time.
> Also, it is kind of weird that arguments require exact match but return
> types do not. Not that we care for consistency anymore...
> --
> Stanislav Malyshev, Software Architect
> SugarCRM: http://www.sugarcrm.com/

---

## #78760 — Andrea Faulds — 2014-11-06T00:26:26+00:00

URL: https://externals.io/message/78608#78760 · In reply to: #78752

> 
> 
> Hi!
> 
> > […]
> 
> This is not good. The base premise of type checks always was if you
> don't use them, they are basically free - you don't load the classes,
> you don't do any work, until you actually need them, and by then your
> class is supposed to be already loaded, so again it's not costly. But if
> just declaring a type could trigger loading of code, this may
> significantly increase the footprint and loading time for classes that
> have many options but only use some of them at the time.

It shouldn’t need to load any code normally, it’s only if an inheriting class changes the type.

> Also, it is kind of weird that arguments require exact match but return
> types do not. Not that we care for consistency anymore…

Yeah, we should probably have arguments be contravariant or covariant.

I was going to argue that covariance is important, but now that I think about it, the most important case is just self. If I can have Foo::foo() return a Foo and Bar::foo() return a Bar, with Bar inheriting from Foo, and this not breaking the invariant type check, I’m happy.

--
Andrea Faulds
http://ajf.me/

---

## #78762 — Andrea Faulds — 2014-11-06T00:57:25+00:00

URL: https://externals.io/message/78608#78762 · In reply to: #78760

> 
> 
> > […]
> 
> Yeah, we should probably have arguments be contravariant or covariant.
> 
> I was going to argue that covariance is important, but now that I think about it, the most important case is just self. If I can have Foo::foo() return a Foo and Bar::foo() return a Bar, with Bar inheriting from Foo, and this not breaking the invariant type check, I’m happy.

What I meant there wasn’t `self`, but actually `static`, I think, which sadly this RFC doesn’t provide. I think that’d be a very useful feature. If we can’t get covariant returns and must go for invariants, I think we’d have to have `static` supported.

I note that Hack also has invariant parameters and covariant return types. There’s probably some good reasoning behind that, I think it’d be worth asking some HHVM people about why that decision was taken, they might provide some useful insight.
--
Andrea Faulds
http://ajf.me/

---

## #78769 — Levi Morrison — 2014-11-06T02:01:12+00:00

URL: https://externals.io/message/78608#78769 · In reply to: #78762

To demonstrate the value of covariance and why `static` alone is not
sufficient, here is a small example:

interface Enumerable extends \IteratorAggregate {
    function getIterator(): Enumerator;
}

class Vector implements Enumerable, \ArrayAccess, \Countable {
    function getIterator(): VectorEnumerator { /* … */ }
}

class VectorEnumerator implements Enumerator, \Countable {
    /* … */
}

This shows why covariance is important for two reasons:
First, it shows that static isn't sufficient. The VectorEnumerator is
not the calling class, so self and static are not applicable.
Second, without covariance you could only declare a return type of
Enumerator for Vector::getIterator(); a calling class couldn't rely on
the properties of a VectorEnumerator that are unique to it, such as
\Countable.

---

## #78789 — Dmitry Stogov — 2014-11-06T07:43:16+00:00

URL: https://externals.io/message/78608#78789 · In reply to: #78769

It's clear, that covariant types are more smart, but not supporting them,
won't prevent access to more specific type properties and methods.
We are not C++ or Java, and we don't need to cast objects to more specific
types.

On the other hand covariance requires all return types to be defined before
class binding.
It may be a serious new problem. For example you won't be able to compile
the following code at all?

<?php
class A {
  function foo(): C {}
}
class B extends A {
  function foo(): C {}
}
class C extends B {
  function foo(): C {}
}
?>

The similar code with argument type hinting works fine.
It's just a first example I could imagine, I believe, we will get more...

Thanks. Dmitry.

On Thu, Nov 6, 2014 at 5:01 AM, Levi Morrison <morrison.levi@gmail.com>
wrote:

> To demonstrate the value of covariance and why `static` alone is not
> sufficient, here is a small example:
>
> interface Enumerable extends \IteratorAggregate {
>     function getIterator(): Enumerator;
> }
>
> class Vector implements Enumerable, \ArrayAccess, \Countable {
>     function getIterator(): VectorEnumerator { /* … */ }
> }
>
> class VectorEnumerator implements Enumerator, \Countable {
>     /* … */
> }
>
> This shows why covariance is important for two reasons:
> First, it shows that static isn't sufficient. The VectorEnumerator is
> not the calling class, so self and static are not applicable.
> Second, without covariance you could only declare a return type of
> Enumerator for Vector::getIterator(); a calling class couldn't rely on
> the properties of a VectorEnumerator that are unique to it, such as
> \Countable.

---

## #78819 — Levi Morrison — 2014-11-06T14:51:53+00:00

URL: https://externals.io/message/78608#78819 · In reply to: #78789

> It's clear, that covariant types are more smart, but not supporting them,
> won't prevent access to more specific type properties and methods.
> We are not C++ or Java, and we don't need to cast objects to more specific
> types.
>
> On the other hand covariance requires all return types to be defined before
> class binding.
> It may be a serious new problem. For example you won't be able to compile
> the following code at all?
>
> <?php
> class A {
>   function foo(): C {}
> }
> class B extends A {
>   function foo(): C {}
> }
> class C extends B {
>   function foo(): C {}
> }
> ?>

I think I have figured out away to do the type checks that would allow
this and current checks to work; I'll report back after some trial and
testing.

---

## #78820 — Andrea Faulds — 2014-11-06T15:00:39+00:00

URL: https://externals.io/message/78608#78820 · In reply to: #78789

> 
> 
> It may be a serious new problem. For example you won't be able to compile
> the following code at all?
> 
> <?php
> class A {
>  function foo(): C {}
> }
> class B extends A {
>  function foo(): C {}
> }
> class C extends B {
>  function foo(): C {}
> }
> ?>
> 
> The similar code with argument type hinting works fine.
> It's just a first example I could imagine, I believe, we will get more...

I can't see any reason that example wouldn't work. The types didn't change, so there's no class loading needed for the covariance check.

--
Andrea Faulds
http://ajf.me/

---

## #78821 — Dmitry Stogov — 2014-11-06T15:08:25+00:00

URL: https://externals.io/message/78608#78821 · In reply to: #78820

Use A or B for return type in B::foo(). It'll lead to compile error anyway
(Class C is not defined).
It's not possible to compile this by design.

Thanks. Dmitry.



>
> > […]
>
> I can't see any reason that example wouldn't work. The types didn't
> change, so there's no class loading needed for the covariance check.
>
> --
> Andrea Faulds
> http://ajf.me/

---

## #78822 — Andrea Faulds — 2014-11-06T15:15:55+00:00

URL: https://externals.io/message/78608#78822 · In reply to: #78821

> 
> 
> Use A or B for return type in B::foo(). It'll lead to compile error anyway (Class C is not defined).
> It's not possible to compile this by design.

Ok, let’s try this:

<?php
class A {
  function foo(): C {}
}
class B extends A {
  function foo(): B {}
}
class C extends B {
  function foo(): C {}
}
?>

I don’t think this can’t be compiled. When you get to B, you know that A doesn’t extend anything, and B doesn’t extend C, so C can’t possibly be a superclass of B, and it would fail, right?

--
Andrea Faulds
http://ajf.me/

---

## #78824 — Dmitry Stogov — 2014-11-06T18:45:10+00:00

URL: https://externals.io/message/78608#78824 · In reply to: #78822

It fails because class C is not known at the point when we check if
A::foo() is compatible with B::foo().
The relation between C and B doesn't really matter in this case (I may
provide another example with relation that must be supported).

Thanks. Dmitry.



>
> > […]
> anyway (Class C is not defined).
> > […]
>
> Ok, let’s try this:
>
> <?php
> class A {
>   function foo(): C {}
> }
> class B extends A {
>   function foo(): B {}
> }
> class C extends B {
>   function foo(): C {}
> }
> ?>
>
> I don’t think this can’t be compiled. When you get to B, you know that A
> doesn’t extend anything, and B doesn’t extend C, so C can’t possibly be a
> superclass of B, and it would fail, right?
>
> --
> Andrea Faulds
> http://ajf.me/
