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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added mixed argument type & return type #2603

Closed
wants to merge 2 commits into from

Conversation

Majkl578
Copy link
Contributor

@Majkl578 Majkl578 commented Jun 30, 2017

This is a proof of concept for built-in mixed type for parameter types and return types.
The behavior of mixed type matches the behavior when no type is specified (thus being implicitly mixed).
Primary motivation for having explicit mixed type is consistence and easier static analysis. In PHP 7.2, mixed types are unfortunately the only type that could not be type hinted upon (true, resource as well, but its future is unclear).
I believe having mixed type would polish the code interface even more, thus having code with 100% explicit type hint coverage.


Before:

<?php
function foo($arg) {
    return $arg;
}

Now:

<?php
function foo(mixed $arg): mixed {
    return $arg;
}

(Note that I barely know the PHP internals so this may be entirely wrong approach.)


TODOs:

  • disalow ?mixed at compile time?
  • add more tests?

This probably needs an RFC, although mixed is already a reserved type. Since I don't have permission to create RFCs, a sponsor would be welcomed (anyone? 馃槆).
Not sure whether it'd be too late for inclusion in 7.2 in case it'd be accepted (probably not yet since there's not even a beta yet).

What do you think? 馃殺

@krakjoe krakjoe added the RFC label Jul 4, 2017
@Majkl578
Copy link
Contributor Author

Majkl578 commented Jul 6, 2017

@sgolemon @remicollet Hi, do you think this would still be acceptable for 7.2?
Scheduled feature freeze for 7.2 is on July 20th and as RFCs require 14 days discussion period + 7-14 days voting period, it's unfortunately after the deadline. :/
But since this would be a tiny language change with no BC breaks, just an alias to existing behavior, I think it'd be okay to eventually fine to introduce in later beta. Or not?

@Majkl578 Majkl578 force-pushed the mixed-type branch 2 times, most recently from 5c0b355 to e1142bb Compare July 6, 2017 21:22
@KalleZ
Copy link
Member

KalleZ commented Jul 6, 2017

I was recently hit by this oddity, I actually thought we reserved it and made it possible with the introduction in 7.0, get this in!

@Majkl578
Copy link
Contributor Author

Majkl578 commented Jul 6, 2017

I actually thought we reserved it and made it possible with the introduction in 7.0,

mixed is reserved as of PHP 7.0, but it has never been implemented (pretty much like object). :/ Having object in 7,2 is just another reason why this would be nice to have in 7.2 as well. 馃憤

@Majkl578
Copy link
Contributor Author

Majkl578 commented Jul 19, 2017

Ping @sgolemon @remicollet - what do you think about this, is it already too late or would it be acceptable as a self-contained addition aliasing current behavior?
Note that I've requested Wiki karma in php.internals to write an RFC over a week ago and got absolutely no reply so far... 馃槥

@krakjoe
Copy link
Member

krakjoe commented Jul 19, 2017

You have missed the official feature freeze (yesterday), while the branch is in pre-release mode, we can't do anything.

The thing to do is let the RFC process run, we can see where we are at the end of that once all discussion and voting is done. It may be acceptable to merge once GA, although this is not my call.

You have permission to write an rfc on wiki.

@Majkl578
Copy link
Contributor Author

Majkl578 commented Jul 19, 2017

@krakjoe Thanks. 馃憤

RFC: https://wiki.php.net/rfc/mixed-typehint

@b1rdex
Copy link
Contributor

b1rdex commented Dec 1, 2017

@Majkl578 have you started RFC discussion?

@Majkl578
Copy link
Contributor Author

Majkl578 commented Dec 1, 2017

Not yet, it's not finished and there is plenty of time for 7.3. :)

@Majkl578
Copy link
Contributor Author

Majkl578 commented Dec 19, 2017

I must admit I have no clue why one build target fails on two of these tests and the other one is green, both Travis and AppVeyor. It was fine before rebase in which I only changed type code in zend_types.h. :/
Can't reproduce any failure localy... Could anyone provide any pointers? Thanks.

@Majkl578
Copy link
Contributor Author

@jordyvandomselaar
Copy link

Why would you typehint a non type? Then just don't typehint -.-, this undoes everything typehinting was supposed to fix.

@b1rdex
Copy link
Contributor

b1rdex commented Dec 20, 2017

@jordyvandomselaar @carusogabriel answer for your question is in first comment 鈥斅爐his feature is supposed to add more clearance to code to show where mixed is really accepted and where just no type hint specified.

@jordyvandomselaar
Copy link

鈥 And not typehinting wouldn't be clear enough? No type hints === no specified type === throw in whatever you'd like.

@Majkl578
Copy link
Contributor Author

I am not going to argue with people who don't understand difference between mixed type and no type, sorry. Look at code that uses 7.1 and maintains BC for another X years. Example: class with 10 recently added methods with typehints, 10 old methods without anything to maintain BC. Now tell anyone whether mixed is intentional or not.

@jordyvandomselaar
Copy link

There isn't a difference between no type and a mixed type. Intentional or not, old code will still accept any type, a.k.a. mixed type. If you're upgrading to php 7.2 I suggest you refactor code that ends up using type hints anyways.

@Htarlov
Copy link

Htarlov commented Dec 22, 2017

I'm not sure it's very good idea.

You always can add comment to hint that type hint is ommited on purpose (with some inforation why it was done).

With mixed type there will be situations when someone asks "please add type hints to your code" or there will be a requirement that code has to be with type hints and then a programmer will add massive amounts of "mixed" type hints - "because deadline".

}
}

class Baz extends bar {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: bar in lower case

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bar

@Taluu
Copy link

Taluu commented May 14, 2018

Bumping, would like to see that one in 7.3 if possible... Because I transformed a few of my interfaces to mixed type, where it makes sense. and not adding any typehint kinda annoys me.

@ostrolucky
Copy link

@Htarlov has very good point. Developers will just start adding mixed typehints mindlessly and everybody will think they are mixed by purpose.

@Taluu
Copy link

Taluu commented May 14, 2018

IMO it's the same really as typing nothing, except that if nothing is typed it really means "meh don't care". with the typehint, it's "tried to care, up to you to care to check". It's semantics, really.

@Majkl578
Copy link
Contributor Author

@Htarlov has very good point. Developers will just start adding mixed typehints mindlessly and everybody will think they are mixed by purpose.

If anyone wants to shoot themselves into foot, it's their choice.

Similar with Error, you can throw TypeError on your own although it's probably not a good idea.

@jordyvandomselaar
Copy link

I still don't think it adds anything but bloat. It's functionality is none, it doesn't help the developer- nor PHP at all. Mixed is implicit if there is no type hint. It literally has no upsides at all.

@Majkl578
Copy link
Contributor Author

Majkl578 commented May 15, 2018

It's functionality is none

If nothing else, it forbids mixing mixed and void.

it doesn't help the developer

Quite the contrary: the developer instantly knows it's explicitly expected to accept/return mixed, instead of being forgotten type hint or legacy PHP 5 pre-7.1/7.2 code.

Mixed is implicit if there is no type hint.

Don't forget that no return type means EITHER mixed OR void.

@JanTvrdik
Copy link

Don't forget that no return type means EITHER mixed OR void.

mixed (being the top type) is supertype of void (being a unit type).

@Majkl578
Copy link
Contributor Author

Are you arguing that mixed does/should not include void?

Yes, please read the discussion/RFC.

@Majkl578
Copy link
Contributor Author

Majkl578 commented Jul 3, 2018

FYI: Based on the discussion and clearing up the inheritance issue with void, we're postponing this for 7.4/8.0 (whichever comes first).

@Majkl578
Copy link
Contributor Author

Majkl578 commented Feb 8, 2019

Published new version: https://wiki.php.net/rfc/mixed-typehint
This PR is not updated yet.

/* Child defines a type, but parent doesn't, violates LSP, unless it's a mixed type */
if (ZEND_TYPE_CODE(fe_arg_info->type) == IS_MIXED) {
return 1;
}
return 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this can be simplified:

Suggested change
return 0;
return ZEND_TYPE_CODE(fe_arg_info->type) == IS_MIXED;

@@ -359,6 +362,9 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
if (proto->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
/* Removing a return type is not valid. */
if (!(fe->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
if (ZEND_TYPE_CODE(proto->common.arg_info[-1].type) == IS_MIXED) {
return 1;
}
return 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this can be simplified:

Suggested change
return 0;
return ZEND_TYPE_CODE(proto->common.arg_info[-1].type) == IS_MIXED;

@dugajean
Copy link

I would like to see this merged

@azjezz
Copy link

azjezz commented Apr 23, 2019

HUGE +1, would love to see this in PHP 7.4 or maybe 8.0, mixed would be extremely useful, specially when we get generics.

Example :

<?php declare(strict_types=1);

interface Normalizer<T> {
  public function normalize(T $data): array;
}

function foo(Normalizer<User> $normalizer, User $user): array {
  return $normalizer->normalize($user);
}
function bar(Normalizer<string> $normalizer, string $data): array {
  return $normalizer->normalize($data);
}
function baz(Normalizer<int> $normalizer, int $number): array {
  return $normalizer->normalize($number);
}
final class DefaultNormalizer implements Normalizer<mixed> {
  public function normalize(mixed $data): array {
   // code
  }
}

$normalizer = new DefaultNormalizer();
foo($normalizer, new User()); // works
bar($normalizer, 'bar'); // works
baz($normalizer, 123); // works

@azjezz
Copy link

azjezz commented Apr 26, 2019

UPDATE : if PHP got generics at some point + #4073 + #2603 , all iterables that are not type hinted, should be considered iterable<arraykey, mixed>

e.g :

function foo(array $arr): void {}

is same as :

function foo(array<arraykey, mixed> $arr): void {}

@Majkl578
Copy link
Contributor Author

@azjeez Yes, it applies to generics in general, not just iterable or arrays. I'll make that clear in the RFC too.

@azjezz
Copy link

azjezz commented Apr 26, 2019

@Majkl578 , this should not be allowed with generics :

interface Foo<T> {}

function foo(Foo $foo): void {}

<T> would need to be specified, specially when there's type constrain :

interface Foo<T as Bar> {}

but i think iterable, ArrayAccess, Iterator ... etc ( existing interfaces / types that use generics ), should have an exception, otherwise requiring <Tk, Tv> will be a HUGE BC break.

@Majkl578
Copy link
Contributor Author

@azjeez That's something up to the Generics RFC to define, in case mixed gets accepted.

@boesing
Copy link

boesing commented Aug 2, 2019

I would prefer having an or condition for multiple types instead of mixed which could be any type.
I dont think that methods, which return mixed, return more than a specific number of pre-defined types.

Most of the time its probably something like:

/** @return string|bool */
public function foo(): mixed;

So a declaration like would make more sense to me.

public function foo(): string | bool;

@kunicmarko20
Copy link

@boesing
Copy link

boesing commented Aug 2, 2019

@kunicmarko20 exactly. Would prefer these types over mixed as mixed is too generic.

@nikic
Copy link
Member

nikic commented Jan 10, 2020

As we now have union types, it probably makes sense to bring this up again.

@Girgias
Copy link
Member

Girgias commented Jan 10, 2020

As we now have union types, it probably makes sense to bring this up again.

Isn't the main contention point as to whether it should include null or not?

@azjezz
Copy link

azjezz commented Jan 10, 2020

Isn't the main contention point as to whether it should include null or not?

IMHO, mixed should include everything, basically :

function foo($bar): void {}

is same as :

function foo(mixed $bar): void {}

note that hacklang already has another type called nonnull which doesn't include null mixed - null : https://docs.hhvm.com/hack/built-in-types/nonnull

@jordyvandomselaar
Copy link

As we now have union types, it probably makes sense to bring this up again.

If we have union types, this doesn't solve any usecases anymore?

@Majkl578 Majkl578 closed this Jan 12, 2020
@Majkl578 Majkl578 reopened this Jan 12, 2020
@Majkl578
Copy link
Contributor Author

@jordyvandomselaar With union types it becomes an alias, a sugar, similar to iterable or nullable types. The use case is still to express explicit interest in mixed value without having to write a full union everywhere.
(Accidentally closed on phone, this PR becomes a bit unmaintainable in there. 馃槄)

@nikic
Copy link
Member

nikic commented Jan 12, 2020

Note that it's not actually possible to write out a mixed type as a union right now, because you're not permitted to type against resource for forward-compatibility reasons.

@jordyvandomselaar
Copy link

@Majkl578 but how would you have the compiler handle it? If you use something, would it complain that you have not handled any other type in your project that it might return? I use Laravel for example, should the compiler complain that I have not checked against a Laravel collection?

I know TypeScript for example, tells me I forgot to check for null when using something that is nullable.

@kocsismate
Copy link
Member

@Majkl578 What's your plan with this? Will you propose mixed for PHP 8?

@acasademont
Copy link
Contributor

This would be great indeed :)

@VincentLanglet
Copy link
Contributor

A mixed argument type would be great.

My example is simple but it would be definitely better to write

function foo(mixed $value): mixed {
   return $value
}

Rather than

function foo($value) {
   return $value
}

Which could be changed to

function foo($value) {
   return;
}

Without any error.

Plus arguing that

function foo($value) {}

And

function foo(mixed $value) {}

Are the same, is like saying

function foo($value) {}

And

/**
 * @param mixed $value
 */
function foo($value) {}

Are the same.
IMHO, it's not. I see interest in the documentation.

@Chi-teck
Copy link

I think without mixed it is hard for static analyzers to identify whether or not missing type hint is a mistake. Also it can be useful when extending/decorating legacy code without type hints.

}
}

class Baz extends bar {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bar

@nikic
Copy link
Member

nikic commented May 23, 2020

New implementation in #5313 has been merged, so closing this one.

@nikic nikic closed this May 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet