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

Implement Parameter Type Widening RFC #2265

Closed
wants to merge 1 commit into from

Conversation

@kelunik
Copy link
Contributor

@kelunik kelunik commented Jan 1, 2017

@kelunik kelunik force-pushed the kelunik:parameter-no-type-variance branch from a9ed7c8 to a7a682a Jan 1, 2017
@php-pulls
Copy link

@php-pulls php-pulls commented Jan 1, 2017

Comment on behalf of krakjoe at php.net:

labelling

@php-pulls php-pulls added the RFC label Jan 1, 2017
@kelunik kelunik force-pushed the kelunik:parameter-no-type-variance branch 2 times, most recently from 16d2400 to 6dce990 Jan 1, 2017
@kelunik kelunik changed the title Implement Parameter No Type Variance RFC Implement Parameter Type Widening RFC Jan 13, 2017
@kelunik kelunik force-pushed the kelunik:parameter-no-type-variance branch 5 times, most recently from 430f972 to 2e85f21 Jan 15, 2017
@kelunik
Copy link
Contributor Author

@kelunik kelunik commented Jan 15, 2017

Rebase onto current master with the type changes, the test failure is unrelated.

@skie
Copy link

@skie skie commented Jan 15, 2017

Of course, send SOLID to hell. Who is Barbara Liskov??? Some mad woman? Of course! Let's allow to break this principles and ideas!

}

if (!ZEND_TYPE_IS_SET(proto_arg_info->type)) {
/* Child defines a type, but parent doesn't, violates LSP */

This comment has been minimized.

@nikic

nikic Jan 15, 2017
Member

I think it would be more intuitive to move this these checks into the argument checking code, rather than have it in the code that is shared between argument and return types. It works either way because return type checking only invokes the function if both types are set, but it's a bit confusing.

This comment has been minimized.

@kelunik

kelunik Jan 15, 2017
Author Contributor

Dunno, it's the place where it was before, so I left it there.

This comment has been minimized.

@guilhermeblanco

guilhermeblanco Jan 16, 2017

This requires decoupling into argument check and return type check, otherwise we'll be allowing widening at return types (basically adding contra-variance support), which is not desired.
Return types only support co-variance.
Argument types only support contra-variance.

This comment has been minimized.

@kelunik

kelunik Jan 16, 2017
Author Contributor

I'll add test cases to check that it doesn't allow contravariant return types and modify as needed.

@nikic
Copy link
Member

@nikic nikic commented Jan 15, 2017

@skie If you're wondering why everyone is confused about your comment: The Liskov substitution principle explicitly allows contravariance on method argument types, which is what is being implemented here. The previous implementation was too restrictive, forbidding some cases that were perfectly valid under LSP.

@b1rdex
Copy link
Contributor

@b1rdex b1rdex commented Jan 16, 2017

@nikic sure, contravariance must be allowed. But how did complete removing of type hint became possible and accepted?

@b1rdex
Copy link
Contributor

@b1rdex b1rdex commented Jan 16, 2017

@kelunik your arguments in rfc are totally mad. Enforcing type hint on method signature is breaking change and library must define new major version in this case. Depended libraries must upgrade extending/usages code and then define new self major version. That's how it must work in semver with bc-compability in mind.
Don't know why would you change language to permit totally wrong use case.

@kelunik
Copy link
Contributor Author

@kelunik kelunik commented Jan 16, 2017

@b1rdex No, it's not a breaking change if your code documented the accepted types before and did these checks manually.

@b1rdex
Copy link
Contributor

@b1rdex b1rdex commented Jan 16, 2017

@kelunik
Copy link
Contributor Author

@kelunik kelunik commented Jan 16, 2017

Please explain where it breaks.

@nikic
Copy link
Member

@nikic nikic commented Jan 16, 2017

@b1rdex It may help you to think of removing a type as specifying a (non-existing) mixed type instead, where mixed is the type semilattice supremum. As such, it is always contravariant (pretty much by definition).

@b1rdex
Copy link
Contributor

@b1rdex b1rdex commented Jan 16, 2017

@nikic good catch.

For anyone interested: following snippet is valid in Java 😮 tested with https://www.compilejava.net/

public class Foo {
    public Foo testParentClass(int $foo) {return new Foo();}
}
public class Bar extends Foo {
    public int testParentClass(String $foo) {return 2;}
}
@b1rdex
Copy link
Contributor

@b1rdex b1rdex commented Jan 16, 2017

@kelunik

Changing signature is always breaking change.
Please explain where it breaks.

in php changing type hints on public api will break extenders before this got merged :)

@nikic
Copy link
Member

@nikic nikic commented Jan 16, 2017

@b1rdex The Java case is somewhat different, because it's not overriding the method, but adding an overload with a different signature. In languages with method overloading variance considerations usually only come in for generic parameters and return types.

@b1rdex
Copy link
Contributor

@b1rdex b1rdex commented Jan 16, 2017

@nikic shame on me, I totally forgot about polymorphism… Hope php will see it someday.

@kelunik
Copy link
Contributor Author

@kelunik kelunik commented Jan 16, 2017

@b1rdex I don't think method overloading is something we'll see in PHP, at least not in the near future. It's rather complicated with weak types and comes with significant overhead IIRC.

in php changing type hints on public api will break extenders before this got merged :)

  1. Changing a type declaration in a subclass will still not be allowed. The only thing that's allowed is the parent having a restriction and the child not having one, thus accepting the same thing the parent accepted and more.
  2. Being able to upgrade existing type hints in docblocks + manual exceptions to use type declarations without breaking backwards compatibility is an important part to move forward with stricter typing.
  3. It didn't break before, it "just" emitted a warning, because PHP can't verify co- / contravariance at compile time currently, because type declarations do not invoke the autoloader. And we don't have method overloading. So all we can do is allowing co- / contravariance instead of method overloading.
@kelunik kelunik force-pushed the kelunik:parameter-no-type-variance branch from 2e85f21 to 5874be7 Jan 17, 2017
@artemzakholodilo
Copy link

@artemzakholodilo artemzakholodilo commented Feb 2, 2017

`abstract class Person
{
private $name;

public function setName($name)
{
    $this->name = $name;
}

public function getName()
{
    return $this->name;
}

protected function __toString()
{
    
}

}

class Foo
{
public function sayHi(Person $person)
{
echo "Hi " . $person->getName();
}
}

class Human extends Person
{}

$foo = new Foo();
$foo->sayHi('Artem');
`

What about this?

@schnittstabil
Copy link

@schnittstabil schnittstabil commented Feb 2, 2017

@artemzakholodilo This PR won't affect your example; $foo->sayHi('Artem'); stills fails, because 'Artem' is not a Person instance:

Warning: The magic method __toString() must have public visibility and cannot be static […]

Fatal error: Uncaught TypeError:
Argument 1 passed to Foo::sayHi() must be an instance of Person, string given, […]

Stack trace:
#0 […].php(34): Foo->sayHi('Artem')
#1 {main}
  thrown in […]
@kelunik kelunik force-pushed the kelunik:parameter-no-type-variance branch from 5874be7 to c419e8f Feb 3, 2017
@kelunik
Copy link
Contributor Author

@kelunik kelunik commented Feb 3, 2017

@guilhermeblanco @nikic I've improved the implementation to use a separate function for args that does the additional checks. I've added an assertion to the current function, so it's only called if both types are set.

if (!ZEND_TYPE_IS_SET(proto_arg_info->type)) {
/* Child defines a type, but parent doesn't, violates LSP */
return 0;
}

This comment has been minimized.

@nikic

nikic Feb 3, 2017
Member

nit: Some space (instead of tab) indents in the above blocks

This comment has been minimized.

@kelunik

kelunik Feb 3, 2017
Author Contributor

Should I put them into a separate commit? The rest of the file already has trimmed whitespace on empty lines.

This comment has been minimized.

@nikic

nikic Feb 3, 2017
Member

As this is newly introduced code, it should be in the same commit.

This comment has been minimized.

@nikic

nikic Feb 3, 2017
Member

Sorry, misunderstanding. I was referring to use of 4-space indentation instead of tabs in the lines above, not the the removal of trailing whitespace, that's fine.

This comment has been minimized.

@kelunik

kelunik Feb 3, 2017
Author Contributor

Ah, right, fixed. I didn't fix the other places using spaces that are not new code.

@nikic nikic self-assigned this Feb 3, 2017
@kelunik kelunik force-pushed the kelunik:parameter-no-type-variance branch from c419e8f to 2da1079 Feb 3, 2017
/* Only one has a type declaration and the other one doesn't */
return 0;
}
ZEND_ASSERT(ZEND_TYPE_IS_SET(fe_arg_info->type) && ZEND_TYPE_IS_SET(fe_arg_info->type));

This comment has been minimized.

@bwoebi

bwoebi Feb 4, 2017
Member

must be proto_arg_info->type once

This comment has been minimized.

@kelunik

kelunik Feb 4, 2017
Author Contributor

Good catch, fixed.

@kelunik kelunik force-pushed the kelunik:parameter-no-type-variance branch from 2da1079 to 6a95578 Feb 4, 2017
@nikic
Copy link
Member

@nikic nikic commented Feb 4, 2017

Merged via 2edc3cf with an UPGRADING note. Thanks!

@JuJuDropThor
Copy link

@JuJuDropThor JuJuDropThor commented May 22, 2017

Are you mad ? Why do you want to violate SOLID and Liskus Substitution like that ? Do you really think that kind of thing will improve the reputation of PHP ?

Shame.

@kelunik kelunik deleted the kelunik:parameter-no-type-variance branch May 22, 2017
@kelunik
Copy link
Contributor Author

@kelunik kelunik commented May 22, 2017

@JuJuDropThor This does not violate the Liskov substitution principle (LSP).

@JuJuDropThor
Copy link

@JuJuDropThor JuJuDropThor commented May 22, 2017

So could you tell me, please, how that could be possible if you change declaration of your method between a type and a subtype ?

@kelunik
Copy link
Contributor Author

@kelunik kelunik commented May 22, 2017

@JuJuDropThor
Copy link

@JuJuDropThor JuJuDropThor commented May 22, 2017

Oh array covariance, great...

You have an array parameter:
public function foo(array $foo)

In the subclass, you could have... everything you want.
public function foo($foo)

Your subclass EverythingClass can't be replaced by an object of type ArrayClass without violating LSP.

First line of the wiki you linked:
Many programming language type systems support subtyping. For instance, if Cat is subtype of Animal, then an expression of type Cat can be used whenever an expression of type Animal could. Variance refers to how subtyping between more complex types (list of Cats versus list of Animals, function returning Cat versus function returning Animal, ...) relates to subtyping between their components.

Well, do as you wish, that RFC is validated anyway. That kind of thing doesn't honor PHP repuration.

@pmmaga
Copy link
Contributor

@pmmaga pmmaga commented May 22, 2017

In this case the contravariance is not the example you mentioned but exactly the other way around. Assuming your EverythngClass, ArrayClass example, and that ArrayClass is the parent and EverythingClass is the child, EverythingClass can replace ArrayClass without breaking LSP. In the example you are giving, you are trying to use the parent to replace the child which is not valid.

@kelunik
Copy link
Contributor Author

@kelunik kelunik commented May 22, 2017

Right, an EverythingClass can't be replaced with an ArrayClass, because that's the wrong way round. EverythingClass is the subtype, not the parent type. Anything that accepts an ArrayClass also works perfectly fine with an EverythingClass, because EverythingClass handles arrays just fine, but also everything else.

@php php locked and limited conversation to collaborators May 22, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

You can’t perform that action at this time.