Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-28 Thread Dmitry Stogov
I prefer option (3) - invariant return types.
Actually, return type compatibility check should follow all the rules for
parameter type compatibility check (may be even reuse or share the code).
This solution may be implemented efficiently and consistently.
It also must be enough for 99% use cases.

Thanks. Dmitry.


On Thu, Nov 27, 2014 at 1:46 PM, Rowan Collins rowan.coll...@gmail.com
wrote:

 Levi Morrison wrote on 25/11/2014 17:08:

1. Do covariant return types; check them at definition time
2. Do covariant return types; check them at runtime
3. Do invariant return types; check them at definition time


 I guess there's also option 4 - do weak invariance, as we do with
 parameters: any combination of types is actually allowed, but any variance
 raises an E_STRICT notice. (This is only true with class inheritance;
 interface implementation is strictly invariant, raising a fatal error if
 the declaration is not typehinted identically.)

 I think my preference would be to implement return types with strict
 invariance (option 3) initially, in order to keep the implementation and
 discussion simple, and get the syntax baked into the language.

 Then immediately look into solutions for covariant return types, and
 possibly also contravariant parameter types (relaxing the fatals for
 interface implementation, and maybe raising the remaining cases above
 E_STRICT for class inheritance).

 If a good solution and implementation can be found in time for 7.0, then
 all the better, but if not, it can be added in 7.1, and no code written for
 the hints added in 7.0 would fail.

 Regards,
 --
 Rowan Collins
 [IMSoP]

 --
 PHP Internals - PHP Runtime Development Mailing List
 To unsubscribe, visit: http://www.php.net/unsub.php




Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-28 Thread Andrea Faulds

 On 28 Nov 2014, at 09:31, Dmitry Stogov dmi...@zend.com wrote:
 
 I prefer option (3) - invariant return types.
 Actually, return type compatibility check should follow all the rules for
 parameter type compatibility check (may be even reuse or share the code).

No, it shouldn't match parameters, that'd break type safety. What's safe for 
parameters is the opposite of what's safe for return types. The exception is 
invariance, which is safe for both.

--
Andrea Faulds
http://ajf.me/
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-28 Thread Rowan Collins

Andrea Faulds wrote on 28/11/2014 10:57:

On 28 Nov 2014, at 09:31, Dmitry Stogov dmi...@zend.com wrote:

I prefer option (3) - invariant return types.
Actually, return type compatibility check should follow all the rules for
parameter type compatibility check (may be even reuse or share the code).

No, it shouldn't match parameters, that'd break type safety. What's safe for 
parameters is the opposite of what's safe for return types. The exception is 
invariance, which is safe for both.


I think you're both saying the same thing: current implementation of 
parameter checking is invariant, proposal is to make return type 
checking invariant. Thus, for now, it can share the implementation.


Later, covariant returns and/or contravariant parameters could be added, 
at which point the checks would need to be split apart again.


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-28 Thread Dmitry Stogov
I didn't get what you mean.
parameters are invariant, invariance, which is safe for both and  it
shouldn't match parameters are contradictory.

Thanks. Dmitry.

On Fri, Nov 28, 2014 at 1:57 PM, Andrea Faulds a...@ajf.me wrote:


  On 28 Nov 2014, at 09:31, Dmitry Stogov dmi...@zend.com wrote:
 
  I prefer option (3) - invariant return types.
  Actually, return type compatibility check should follow all the rules for
  parameter type compatibility check (may be even reuse or share the code).

 No, it shouldn't match parameters, that'd break type safety. What's safe
 for parameters is the opposite of what's safe for return types. The
 exception is invariance, which is safe for both.

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


Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-28 Thread Andrea Faulds

 On 28 Nov 2014, at 12:27, Dmitry Stogov dmi...@zend.com wrote:
 
 I didn't get what you mean.
 parameters are invariant, invariance, which is safe for both and  it 
 shouldn't match parameters are contradictory.

Well, that’s why I said “the exception is invariance”.
--
Andrea Faulds
http://ajf.me/





--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-28 Thread Levi Morrison
 I prefer option (3) - invariant return types.
 Actually, return type compatibility check should follow all the rules for
 parameter type compatibility check (may be even reuse or share the code).

Realistically there isn't much code to share, especially since the two
structures are incompatible. That is, they are incompatible until
`zend_string *` is used in parameters (which I'm hopeful will happen).

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-28 Thread Dmitry Stogov
Hi Levi,

if you remember, in my patch return_type was actually stored in
arg_info[-1].
it was mainly done for unification and to allow return type hinting for
internal functions (they already use arg_info[-1])
So the strictures may be compatible.
See https://gist.github.com/dstogov/8deb8b17e41c1a5abf88

I also thought about zend_string* usage in arg_info
Please review the patch if you missed it.
https://gist.github.com/dstogov/aa452a47ec30a8cb6eb5

The patches may be outdated, but they worked.
I may care about the final implementation if you agree with invariant
return type.

Thanks. Dmitry.



On Fri, Nov 28, 2014 at 7:51 PM, Levi Morrison le...@php.net wrote:

  I prefer option (3) - invariant return types.
  Actually, return type compatibility check should follow all the rules for
  parameter type compatibility check (may be even reuse or share the code).

 Realistically there isn't much code to share, especially since the two
 structures are incompatible. That is, they are incompatible until
 `zend_string *` is used in parameters (which I'm hopeful will happen).



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-27 Thread Rowan Collins

Lazare Inepologlou wrote on 26/11/2014 10:21:

* Parameter types are contravariant, otherwise they are not type sound.
Yet, contravariance in general is of little interest (I cannot think of any
practical example), so invariance is a good compromise.


After reading the Wikipedia article, I've been thinking of some 
practical example of contravariance in PHP. One involves the Iterator 
and Traversable interfaces: imagine we have this class:


class Foo { public function iterate(Iterator $i) { /*...*/ } }

Now we make a sub-class which implements its iterate method using a 
foreach() loop, so can transparently accept any Traversable:


class Bar extends Foo { public function iterate(Traversable $t) { 
/*...*/ } }


This requires contravariance, because the child class accepts everything 
the parent class would, but doesn't have an identical type hint because 
it also accepts more.



A more involved example would be this:

// Two types of user, both extending a base class
abstract class User { abstract function getDisplayName(); /*...*/ }
class VisitingUser extends User { function getDisplayName() { return 
'Anonymous Coward'; } /*...*/ }
class RegisteredUser extends User { function getDisplayName() { /*...*/ 
} function getUserID { /*...*/ } /*...*/ }


// An interface for injecting observers of social events carried out by 
registered users
interface SocialEventListener { function handleUserEvent( RegisteredUser 
$u, Event $e ); }
// An implementing class can safely use methods only present for 
registered users without additional type checks
class UserTimelineWriter implements SocialEventListener { function 
handleUserEvent( RegisteredUser $u, Event $e ) { $user_id = 
$u-getUserID(); /*...*/ } }


// However, a more general observer might not use the user ID, and so 
could be reused for events with any kind of user
interface UserActionListener { function handleUserEvent( User $u, Event 
$e ); }
class EventLogger implements UserActionListener, SocialEventListener{ 
function handleUserEvent( User $u, Event $e ) { /* code relying only on 
$u-getDisplayName() ... */ } }


Without parameter contravariance, there is no way to achieve this. Since 
PHP doesn't have method overloading, you can't add a second version of 
handleUserEvent which accepts only RegisteredUser arguments, so you have 
to either change the name of the method in one of the interfaces, or 
create an entire adapter class just to change the type hint.



Obviously, the exact details here are contrived to make the point, but 
they don't seem all that far-fetched to me.


Regards,
--
Rowan Collins
[IMSoP]

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-27 Thread Rowan Collins

Levi Morrison wrote on 25/11/2014 17:08:

   1. Do covariant return types; check them at definition time
   2. Do covariant return types; check them at runtime
   3. Do invariant return types; check them at definition time


I guess there's also option 4 - do weak invariance, as we do with 
parameters: any combination of types is actually allowed, but any 
variance raises an E_STRICT notice. (This is only true with class 
inheritance; interface implementation is strictly invariant, raising a 
fatal error if the declaration is not typehinted identically.)


I think my preference would be to implement return types with strict 
invariance (option 3) initially, in order to keep the implementation and 
discussion simple, and get the syntax baked into the language.


Then immediately look into solutions for covariant return types, and 
possibly also contravariant parameter types (relaxing the fatals for 
interface implementation, and maybe raising the remaining cases above 
E_STRICT for class inheritance).


If a good solution and implementation can be found in time for 7.0, then 
all the better, but if not, it can be added in 7.1, and no code written 
for the hints added in 7.0 would fail.


Regards,
--
Rowan Collins
[IMSoP]

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-26 Thread Simon Schick
On Tue, Nov 25, 2014 at 11:42 PM, Nikita Popov nikita@gmail.com wrote:
 On Tue, Nov 25, 2014 at 11:13 PM, Marc Bennewitz dev@mabe.berlin wrote:


 Am 25.11.2014 um 22:43 schrieb Levi Morrison:

  On Tue, Nov 25, 2014 at 2:07 PM, Marc Bennewitz dev@mabe.berlin wrote:

 I think it's required to do the type check on runtime (Option 2) because
 one of the use cases for return type-hint are factories and such often do
 instantiation in base of unknown string values:

 class MyFactory {
  public static function factory($name) : AdapterInterface {
  $class = 'MyNamespace\Adapter\' . $name;
  return $class();
  }
 }

 It seems that I did not explain this clearly enough; I apologize. The
 variance has to do with the declared type in the function signature
 when inheritance is involved, not the type of the value returned by
 the function.

 For instance, under any of the three options this code will work just
 fine:

 class Foo {}
 class Goo  extends Foo {}

 class FooFactory {
  function create(): Foo { return new Goo(); }
 }

 As long as the return value from FooFactory::create returns Foo or a
 subtype of Foo (such as Goo), then it will work.

 The variance that is under discussion in this thread is about the
 declared return type in the signature:

 class GooFactory extends FooFactory {
  function create(): Goo {}
 }

 In this case, GooFactory::create() declares a return type of Goo,
 which is a subtype of Foo [the return type of the inherited method
 FooFactory::create()]. This is a covariant return type.

 If we choose option 3, the only possible return type for
 GooFactory::create is Foo.

 Hopefully this clarifies the issue.

 Yes it does - thank you for explanation - my mistake :/

 Option 3 is a no go not from OOP perspective and from consistency pov as
 we already allow this in type-hint:

 class FooFactory {
 function create(Foo $foo): Foo { return $foo; }
 }

 class GooFactory extends FooFactory {
 function create(Goo $goo): Goo { return $goo; }

 }


 This is not correct. Parameter typehints in PHP are invariant, so you are
 not allowed to change them during inheritance. However LSP violations
 during inheritance of *non-abstract* methods currently uses a very low
 error level (E_STRICT), so you probably didn't notice. If you try the same
 thing with an interface method or an explicitly abstract method, you will
 receive a fatal error:

 interface I1 {
 function foo(A $a);
 }
 class C1 implements I1 {
 function foo(B $b) { ... }
 }

 This code snippet will result in a fatal error, because it violates type
 invariance.

 Nikita

Hi, all

As in case of compatibility, I would think about the following:

A return type should be the same, or an instance of a class, extending
the the required class. This way, you have all methods and properties
you would expect.

But as of the parameters, it should be (if we allow anything different
than the class/interface expected), a class or an interface the
parent-one extends or implements. I know, that this one sounds strange
and I can't come up with a practical way. Let me put that one into an
example:

See this example:

class Bar {}
class Foo extends Bar {}
class Goo extends Foo {}

interface ITester {
function create(Goo $foo) : Bar;
}

class BarTester implements ITester {
function create(Bar $foo) : Bar { return $foo; }
}

class FooTester extends BarTester {
function create(Foo $foo) : Foo { return $foo; }
}

class GooTester extends FooTester {
function create(Goo $goo)  : Goo { return $goo; }
}

$testers = array( new BarTester(), new FooTester(), new GooTester() );

/** @var $testers ITesters[] */
foreach($testers as $tester) {

$res = $tester-create(new Goo());
/** @var $res Bar (or some instance extending it) */
}

If you have an instance of ITester, you expect it to accept an
instance of Goo. As FooTester is implemented, it will always accept
Goo as instance here - yea - even more. It also accepts Foo instances
as parameter.

But as I expect the create method to return an instance of Foo (and to
make usage of any method, Foo has), the method could also return an
instance of Goo, because it has all the functionality, Foo has, and
even more.

In this example it may sounds weird and confusing, but if you take
these two things apart, it makes actually sense - each for itself.

Is there something I didn't think about, or that would get it to crash?

Bye
Simon

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-26 Thread Lazare Inepologlou
2014-11-25 23:42 GMT+01:00 Nikita Popov nikita@gmail.com:

 On Tue, Nov 25, 2014 at 11:13 PM, Marc Bennewitz dev@mabe.berlin wrote:

 
  Am 25.11.2014 um 22:43 schrieb Levi Morrison:
 
   On Tue, Nov 25, 2014 at 2:07 PM, Marc Bennewitz dev@mabe.berlin
 wrote:
 
  I think it's required to do the type check on runtime (Option 2)
 because
  one of the use cases for return type-hint are factories and such often
 do
  instantiation in base of unknown string values:
 
  class MyFactory {
   public static function factory($name) : AdapterInterface {
   $class = 'MyNamespace\Adapter\' . $name;
   return $class();
   }
  }
 
  It seems that I did not explain this clearly enough; I apologize. The
  variance has to do with the declared type in the function signature
  when inheritance is involved, not the type of the value returned by
  the function.
 
  For instance, under any of the three options this code will work just
  fine:
 
  class Foo {}
  class Goo  extends Foo {}
 
  class FooFactory {
   function create(): Foo { return new Goo(); }
  }
 
  As long as the return value from FooFactory::create returns Foo or a
  subtype of Foo (such as Goo), then it will work.
 
  The variance that is under discussion in this thread is about the
  declared return type in the signature:
 
  class GooFactory extends FooFactory {
   function create(): Goo {}
  }
 
  In this case, GooFactory::create() declares a return type of Goo,
  which is a subtype of Foo [the return type of the inherited method
  FooFactory::create()]. This is a covariant return type.
 
  If we choose option 3, the only possible return type for
  GooFactory::create is Foo.
 
  Hopefully this clarifies the issue.
 
  Yes it does - thank you for explanation - my mistake :/
 
  Option 3 is a no go not from OOP perspective and from consistency pov as
  we already allow this in type-hint:
 
  class FooFactory {
  function create(Foo $foo): Foo { return $foo; }
  }
 
  class GooFactory extends FooFactory {
  function create(Goo $goo): Goo { return $goo; }
 
  }
 

 This is not correct. Parameter typehints in PHP are invariant, so you are
 not allowed to change them during inheritance. However LSP violations
 during inheritance of *non-abstract* methods currently uses a very low
 error level (E_STRICT), so you probably didn't notice. If you try the same
 thing with an interface method or an explicitly abstract method, you will
 receive a fatal error:

 interface I1 {
 function foo(A $a);
 }
 class C1 implements I1 {
 function foo(B $b) { ... }
 }

 This code snippet will result in a fatal error, because it violates type
 invariance.


Let's not compare these two:

* Parameter types are contravariant, otherwise they are not type sound.
Yet, contravariance in general is of little interest (I cannot think of any
practical example), so invariance is a good compromise.

* Return types are covariant. There are many useful examples already
mentioned in this mailing list.

http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Covariant_method_return_type



Lazare INEPOLOGLOU
Ingénieur Logiciel


Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-26 Thread Rowan Collins
On 26 November 2014 10:21:12 GMT, Lazare Inepologlou linep...@gmail.com wrote:
http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Covariant_method_return_type

Can I just recommend that everyone interested in this discussion read that 
whole article (at least until it gets into the guts of generics, which gets 
more and more complex). It explains the concepts extremely clearly, both in 
their theoretical basis and their practical implementation and limitations.

I often worry that PHP is designed too much around examples and use cases, 
rather than more formal theoretical foundations, so I'm pleased this 
conversation has led me to learn those concepts. Obviously, that article points 
out, there is sometimes value in ignoring the theoretically pure in favour of 
the practical, but the adage applies that you should first understand the rules 
before deciding to break them.

Regards,
-- 
Rowan Collins
[IMSoP]


-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-26 Thread Stanislav Malyshev
Hi!

 class FooFactory {
 function create(Foo $foo): Foo { return $foo; }
 }

 class GooFactory extends FooFactory {
 function create(Goo $goo): Goo { return $goo; }
 }
 OK HHVM allows it - we also allow it but trigger an E_STRICT error
 @see http://3v4l.org/UhtOb

This is because this code has LSP violation - if you have an object
about which you know it's typed as FooFactory, you should be able to
call it with any Foo object. But if this object is a GooFactory instead,
now not any Foo would serve, but only a subset of them - namely, Goo.
This clearly violates the principle everything good for the parent must
be good for the child. Since PHP is a kind and nurturing language, we
only produce E_STRICT, some other languages would refuse to accept such
thing or interpret it as two different methods.
See also:
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Covariant_method_argument_type

-- 
Stas Malyshev
smalys...@gmail.com

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-25 Thread Levi Morrison
 It should be mentioned for Option 3 that covariant return types are not 
 supported just at the definition level . Even with invariant return types the 
 following is perfectly fine:
 class A {
   function foo(): B { return new B; }
 }

 class B extends A {
   function foo(): B { return new C; }
 }

Yes, this is correct; thank you for the clarification. The phrase
covariant return types in the RFC and in my message above refers to
the declared type in the function signature, not the type returned by
the method. The value that is returned can always be subtype of the
declared type.

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-25 Thread Marc Bennewitz

I think it's required to do the type check on runtime (Option 2) because
one of the use cases for return type-hint are factories and such often do
instantiation in base of unknown string values:

class MyFactory {
public static function factory($name) : AdapterInterface {
$class = 'MyNamespace\Adapter\' . $name;
return $class();
}
}

Marc

Am 25.11.2014 um 18:08 schrieb Levi Morrison:

Dear Internals,

As you may remember, I closed the voting on the return types
[RFC](https://wiki.php.net/rfc/returntypehinting) because of a design
flaw that was found during the voting process. The purpose of this
discussion thread is to explain the various options for checking
return type compatibility with parent methods that I think are viable,
to show some of the benefits and drawbacks of each option, and to
solicit feedback on which options you prefer and why. As such, it's
much longer than a normal message I would send.

The following code demonstrates the design flaw mentioned above:

 class A {
 function foo(): B { return new B; }
 }

 class B extends A {
 function foo(): C { return new C; }
 }

 class C extends B {}

 $b = new B;
 $c = $b-foo();

I've also used it because it can adequately show the differences in
how each of the following options work:

   1. Do covariant return types; check them at definition time
   2. Do covariant return types; check them at runtime
   3. Do invariant return types; check them at definition time

Option 1: Covariant return types with definition time checking
--
This is the option that is currently implemented in my pull request.
This option catches all return type variance issues whether code is
used or not; if it got included it is checked. This means return type
variance issues can't bite you later.

When class B is defined, the engine needs to check that the method
`B::foo` has a compatible return type with `A::foo`, in this case that
equates to checking that C is compatible with type B. This would
trigger autoloading for C but it will fail in this case because C is
defined in the same file. Note that there are ways we could fix this
issue, but not reliably because of conditionally defined classes
(defining classes in if blocks, for example) and other such dynamic
behavior. Even if we could fix this issue, there is still an issue if
A, B and C were defined in separate files and then included. You
couldn't require them in any order for it to work; you would have to
autoload.

Option 2: Covariant return types with runtime checking
--
This option would do the variance check when the method is used (or
potentially when the class is instantiated, or whichever comes first).
Regardless of the exact details of how this method is implemented, the
above code would work because the first time class B is used in any
way occurs after C is defined. This would also work if you separated
them out into different files

This option would be slower than option 1, but cannot quantify it
because it is not yet implemented. I suspect there would be a way to
cache the result of the variance check to not have to do it on every
instantiation or invocation, so this may be negligble.

This option has the drawback that inheritance problems can exist in
the code and won't be discovered until the code is ran. Let me repeat
that to make sure that everyone understand it: if you have return type
variance errors in your code, they would not be detected until you try
to use the class or method.

Option 3: Invariant return types with definition time checking
--
This means that the declared types must *exactly match* after
resolving aliases such as parent and self. The advantage of this
option is that the inheritance check can be done at definition time in
all cases without triggering an autoload. As such it is a bit simpler
to implement and is slightly faster to do so. The obvious downside is
that it can't support covariant return types.

---

Note that C++ and Java (and most modern languages) support covariant
return types, but C# supports only invariant return types.

Which of these methods do you prefer the most, and which do you prefer
the least, and why?




--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-25 Thread Levi Morrison
On Tue, Nov 25, 2014 at 2:07 PM, Marc Bennewitz dev@mabe.berlin wrote:
 I think it's required to do the type check on runtime (Option 2) because
 one of the use cases for return type-hint are factories and such often do
 instantiation in base of unknown string values:

 class MyFactory {
 public static function factory($name) : AdapterInterface {
 $class = 'MyNamespace\Adapter\' . $name;
 return $class();
 }
 }

It seems that I did not explain this clearly enough; I apologize. The
variance has to do with the declared type in the function signature
when inheritance is involved, not the type of the value returned by
the function.

For instance, under any of the three options this code will work just fine:

class Foo {}
class Goo  extends Foo {}

class FooFactory {
function create(): Foo { return new Goo(); }
}

As long as the return value from FooFactory::create returns Foo or a
subtype of Foo (such as Goo), then it will work.

The variance that is under discussion in this thread is about the
declared return type in the signature:

class GooFactory extends FooFactory {
function create(): Goo {}
}

In this case, GooFactory::create() declares a return type of Goo,
which is a subtype of Foo [the return type of the inherited method
FooFactory::create()]. This is a covariant return type.

If we choose option 3, the only possible return type for
GooFactory::create is Foo.

Hopefully this clarifies the issue.

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-25 Thread Marc Bennewitz


Am 25.11.2014 um 22:43 schrieb Levi Morrison:

On Tue, Nov 25, 2014 at 2:07 PM, Marc Bennewitz dev@mabe.berlin wrote:

I think it's required to do the type check on runtime (Option 2) because
one of the use cases for return type-hint are factories and such often do
instantiation in base of unknown string values:

class MyFactory {
 public static function factory($name) : AdapterInterface {
 $class = 'MyNamespace\Adapter\' . $name;
 return $class();
 }
}

It seems that I did not explain this clearly enough; I apologize. The
variance has to do with the declared type in the function signature
when inheritance is involved, not the type of the value returned by
the function.

For instance, under any of the three options this code will work just fine:

class Foo {}
class Goo  extends Foo {}

class FooFactory {
 function create(): Foo { return new Goo(); }
}

As long as the return value from FooFactory::create returns Foo or a
subtype of Foo (such as Goo), then it will work.

The variance that is under discussion in this thread is about the
declared return type in the signature:

class GooFactory extends FooFactory {
 function create(): Goo {}
}

In this case, GooFactory::create() declares a return type of Goo,
which is a subtype of Foo [the return type of the inherited method
FooFactory::create()]. This is a covariant return type.

If we choose option 3, the only possible return type for
GooFactory::create is Foo.

Hopefully this clarifies the issue.

Yes it does - thank you for explanation - my mistake :/

Option 3 is a no go not from OOP perspective and from consistency pov as 
we already allow this in type-hint:


class FooFactory {
function create(Foo $foo): Foo { return $foo; }
}

class GooFactory extends FooFactory {
function create(Goo $goo): Goo { return $goo; }
}



--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-25 Thread Marc Bennewitz


Am 25.11.2014 um 23:13 schrieb Marc Bennewitz:


Am 25.11.2014 um 22:43 schrieb Levi Morrison:

On Tue, Nov 25, 2014 at 2:07 PM, Marc Bennewitz dev@mabe.berlin wrote:
I think it's required to do the type check on runtime (Option 2) 
because
one of the use cases for return type-hint are factories and such 
often do

instantiation in base of unknown string values:

class MyFactory {
 public static function factory($name) : AdapterInterface {
 $class = 'MyNamespace\Adapter\' . $name;
 return $class();
 }
}

It seems that I did not explain this clearly enough; I apologize. The
variance has to do with the declared type in the function signature
when inheritance is involved, not the type of the value returned by
the function.

For instance, under any of the three options this code will work just 
fine:


class Foo {}
class Goo  extends Foo {}

class FooFactory {
 function create(): Foo { return new Goo(); }
}

As long as the return value from FooFactory::create returns Foo or a
subtype of Foo (such as Goo), then it will work.

The variance that is under discussion in this thread is about the
declared return type in the signature:

class GooFactory extends FooFactory {
 function create(): Goo {}
}

In this case, GooFactory::create() declares a return type of Goo,
which is a subtype of Foo [the return type of the inherited method
FooFactory::create()]. This is a covariant return type.

If we choose option 3, the only possible return type for
GooFactory::create is Foo.

Hopefully this clarifies the issue.

Yes it does - thank you for explanation - my mistake :/

Option 3 is a no go not from OOP perspective and from consistency pov 
as we already allow this in type-hint:


class FooFactory {
function create(Foo $foo): Foo { return $foo; }
}

class GooFactory extends FooFactory {
function create(Goo $goo): Goo { return $goo; }
}

OK HHVM allows it - we also allow it but trigger an E_STRICT error
@see http://3v4l.org/UhtOb



--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC][Discussion] Return Type Variance Checking

2014-11-25 Thread Nikita Popov
On Tue, Nov 25, 2014 at 11:13 PM, Marc Bennewitz dev@mabe.berlin wrote:


 Am 25.11.2014 um 22:43 schrieb Levi Morrison:

  On Tue, Nov 25, 2014 at 2:07 PM, Marc Bennewitz dev@mabe.berlin wrote:

 I think it's required to do the type check on runtime (Option 2) because
 one of the use cases for return type-hint are factories and such often do
 instantiation in base of unknown string values:

 class MyFactory {
  public static function factory($name) : AdapterInterface {
  $class = 'MyNamespace\Adapter\' . $name;
  return $class();
  }
 }

 It seems that I did not explain this clearly enough; I apologize. The
 variance has to do with the declared type in the function signature
 when inheritance is involved, not the type of the value returned by
 the function.

 For instance, under any of the three options this code will work just
 fine:

 class Foo {}
 class Goo  extends Foo {}

 class FooFactory {
  function create(): Foo { return new Goo(); }
 }

 As long as the return value from FooFactory::create returns Foo or a
 subtype of Foo (such as Goo), then it will work.

 The variance that is under discussion in this thread is about the
 declared return type in the signature:

 class GooFactory extends FooFactory {
  function create(): Goo {}
 }

 In this case, GooFactory::create() declares a return type of Goo,
 which is a subtype of Foo [the return type of the inherited method
 FooFactory::create()]. This is a covariant return type.

 If we choose option 3, the only possible return type for
 GooFactory::create is Foo.

 Hopefully this clarifies the issue.

 Yes it does - thank you for explanation - my mistake :/

 Option 3 is a no go not from OOP perspective and from consistency pov as
 we already allow this in type-hint:

 class FooFactory {
 function create(Foo $foo): Foo { return $foo; }
 }

 class GooFactory extends FooFactory {
 function create(Goo $goo): Goo { return $goo; }

 }


This is not correct. Parameter typehints in PHP are invariant, so you are
not allowed to change them during inheritance. However LSP violations
during inheritance of *non-abstract* methods currently uses a very low
error level (E_STRICT), so you probably didn't notice. If you try the same
thing with an interface method or an explicitly abstract method, you will
receive a fatal error:

interface I1 {
function foo(A $a);
}
class C1 implements I1 {
function foo(B $b) { ... }
}

This code snippet will result in a fatal error, because it violates type
invariance.

Nikita