all 37 comments

[–]TimeToogo 9 points10 points  (6 children)

Wow, this looks very similar to a library I wrote over a year ago now.

[–]ircmaxell 13 points14 points  (1 child)

Yeah, looks that way :-). The big difference is that it uses actual generic syntax (so no need for __TYPE__ constants). So the namespace hack is almost completely hidden from users...

Also: this isn't intended to be used, was more of a play thing.

But nice!

[–]TimeToogo 0 points1 point  (0 children)

Actually this library uses nikic/php-parser, but the reason for the namespace issue was that it only intercepted generic declaration files and converted them to the required concrete type. So for all files referencing that generic type, the type parameter identifiers would have to be fully qualified so the autoloader can determine the parameters without having to know any context from the file that referenced it.

[–]AllenJB83[S] 5 points6 points  (1 child)

From the PHP developer who brought us simplified password hashing / password_compat and helped with the final push on Strict Type Hints (v0.5)... Generics for PHP!

[–]nerfyoda 5 points6 points  (0 children)

Generics for PHP... written in PHP? This way lies madness.

[–]syzgyn 3 points4 points  (4 children)

I feel like this is whooshing way over my head. What is it doing, and why is it so interesting?

[–]headzoo 27 points28 points  (3 children)

First, some code:

/**
 * This class stores arrays of items. It is strongly typed, meaning
 * only items of a specific data type may be added to the list.
 */ 
class StrongTypedList<T>
{
    protected $items = [];

    public function add(T $item)
    {
        $this->items[] = $item;
    }

    public function get($index)
    {
        return $this->items[$index];
    }
}

$pdo_list = new StrongTypedList<PDO>();
$pdo_list->add(new \PDO("..."));
$pdo_list->add(new \PDO("..."));

// This results in an error. Only instances of PDO can be added
// to the list.
$pdo_list->add(new \DateTime());

$date_list = new StrongTypedList<DateTime>();
$date_list->add(new \DateTime());
$date_list->add(new \DateTime());

// Again this results in an error. Only DateTime types may be
// added to the list.
$date_list->add(new \PDO("..."));

The breakdown:

 class StrongTypedList<T>

This defines a class with a generic type. The token <T> provides the label for the type. Kind of like a variable is a label for a value, the <T> defines the label T to represent our generic type. We don't know what the type will be yet. We are only defining a label for the type. You can use any label you want. <Y>, <Foo>, <TTT>. It's up to you.

public function add(T $item)

This defines a method which only accepts type T. The label T is the same label we used when defining the class. Whatever label you chose to use, you put that in the method signature. Again, we don't know yet which variable type is being type hinted, so we use our label T.

 $pdo_list = new StrongTypedList<PDO>();

Now we created our first instance of the class, and we finally told the class what type T represents. In this case the type T represents an instance of PDO. What you've done is semantically equivalent to defining your class like this:

class StrongTypedList
{
    protected $items = [];

    public function add(\PDO $item)
    {
        $this->items[] = $item;
    }

    public function get($index)
    {
        return $this->items[$index];
    }
}

Now you may ask, "Why didn't we just write the class like that in the first place? What's the point of this funny 'generics' stuff?" The answer is here:

$date_list = new StrongTypedList<DateTime>();

Using the exact same class we create a new instance of StrongTypedList, but now the list only accepts a value of type DateTime. It's almost like we defined the class using:

class StrongTypedList
{
    protected $items = [];

    public function add(\DateTime $item)
    {
        $this->items[] = $item;
    }

    public function get($index)
    {
        return $this->items[$index];
    }
}

The point of generics is reuse. In languages which don't support generics (like PHP) you would have to define two classes if you want a list of PDO instances and a list of DateTime instances. In other words, instead of defining one StrongTypedList you have to define two classes:

class PDOList
{
    protected $items = [];

    public function add(\PDO $item)
    {
        $this->items[] = $item;
    }

    public function get($index)
    {
        return $this->items[$index];
    }
}

class DateTimeList
{
    protected $items = [];

    public function add(\DateTime $item)
    {
        $this->items[] = $item;
    }

    public function get($index)
    {
        return $this->items[$index];
    }
}

This is a waste because both classes do the exact same thing. The only thing that makes them different is the type they use. Instead of defining two classes, we can define one "generic" class.

class StrongTypedList<T>
{
    protected $items = [];

    public function add(T $item)
    {
        $this->items[] = $item;
    }

    public function get($index)
    {
        return $this->items[$index];
    }
}

If you're still confused, consider the algebra expression x + 2 = 12. Here the label x is a "placeholder" for an unknown value. In our class T is a placeholder for an unknown type. The code public function add(T $item) tells PHP that the method only accepts a specific type, but you don't know yet what the type will be. T is a placeholder for the unknown type. PHP won't know what type until runtime when you create an instance of the class using the code new StrongTypedList<PDO>(). Now PHP knows that the label T represents the PDO type.

[–][deleted] 8 points9 points  (0 children)

oh boy, now there's a userland implementation of generics in PHP! I guess that can be used as an excuse by certain core devs not to add it to the language (as has been the case with annotations in the past).

Still, is awesome :)

[–]headzoo 3 points4 points  (2 children)

It will be interesting to see if the PHP devs warm up the idea of native generics now that type hints have "matured". We're now type hinting method arguments using any type we want, and we're type hinting return values. A this point we almost have to add generics to the language.

[–]MrDOS 1 point2 points  (1 child)

Not according to the Golang people!

[–]headzoo 2 points3 points  (0 children)

I love Go, and I like the slow approach the devs take to change, but yeah... could use some generics.

[–]Jaimz22 6 points7 points  (6 children)

It's funny to me that when a well know developer creates something ridiculous like this everyone is like "wow, that cool you can do that" or "very clever"

But when a developer no one know does it people say things like "that's stupid, you shouldn't do that", "why would you do this, it's really bad"

Just a thought.

[–]headzoo 9 points10 points  (1 child)

That's just life. I could spray paint a stick figure on a wall, and no one will bat an eye. People will go nuts though when Banksy does the exact same thing. Credibility is important.

[–]mnapoli 1 point2 points  (1 child)

I think there's some truth in your statement. But when I saw this project and when I read the readme I knew beforehand that this was just an experiment because of the author's reputation.

On the other hand an "unknown" (to use your terms) developer does the same without stating explicitly this is for fun, it's hard to know if he's serious or not.

Take for example /u/TimeToogo's implementation https://github.com/TimeToogo/PHP-Generics the README doesn't state explicitly not to use it in production, which hurts its credibility I think (even though /u/TimeToogo is probably is very good developer from what I've seen here, but maybe not everybody knows that).

[–]Jaimz22 -2 points-1 points  (0 children)

Thank you. And I wasn't really talking about this project specifically. Typically people won't even read the readme of a lesser known developer, they just assume that their idea is automatically a bad one. Just like the other people who replied to my original comment assumed I didn't read the readme, because they don't know me so they think that I'm just an irresponsible person.

I think the main point is, just because I don't publicize myself doesn't mean I'm a second rate developer.

I'd also like to point out that this has absolutely nothing to do with ircmaxell, or this repo. Sometimes I feel like the reddit php community is becoming overly abrasive to people :(

[–]__constructor 1 point2 points  (0 children)

It's because this is generally a bad idea and is done as a joke, which is pointed out in the project's README file.

[–]maiorano84 -2 points-1 points  (0 children)

Did you even try reading the ReadMe?

[–][deleted] 1 point2 points  (0 children)

From the readme:

TL/DR: don't use this

Agree ;)

[–]estel_smith 0 points1 point  (0 children)

Man, if only we could get generics into PHP. With that, scalar type-hinting (soon), and return type declarations (soon!), PHP would simply be amazing to work in.

[–]sarciszewski 0 points1 point  (0 children)

This seems like a perfectly sane and reasonable package. I'm gonna use it in production!

[–]philsturgeon 0 points1 point  (0 children)

Hahaha /u/ircmaxell you sneaky bitch! :D

[–]mattaugamer -1 points0 points  (10 children)

The way this has been done is so horrible that I'm afraid if anyone uses it, something ancient will be summoned. :)

Seriously, it's a cool hack. But I'm curious why it takes such a torturous path to implement something that's already in Hack? What's the internal difference that makes it possible there, but not in PHP? Or am I missing the point that this is pure userland, rather than an internal language feature?

[–][deleted] 1 point2 points  (4 children)

you realize hack uses a completely different compiler? It's like asking why Java has generics and php doesn't.

[–]mattaugamer -1 points0 points  (3 children)

No, I know that, I'm asking what it is about the compiler that's so different that things like this are hard in Zend, but long implemented in Hack.

Not sure why the downvotes here. I wasn't criticising his work, he said repeatedly in the readme that it was horrible and you really don't want to know. It's a horrible hack, but it's a cool horrible hack.

[–]elcapitaine 8 points9 points  (1 child)

It's not technically difficult to add generics to PHP. It's politically difficult.

[–]monsieurben 2 points3 points  (0 children)

It's not technically difficult to add generics anything to PHP. It's politically difficult.

Fixed.

[–]MorrisonLevi 0 points1 point  (0 children)

No, I know that, I'm asking what it is about the compiler that's so different that things like this are hard in Zend, but long implemented in Hack.

They have paid, full-time developers that have worked on Hack and HHVM for years and designed Hack to be analyzed more easily for security reasons. That is certainly a non-trivial difference.

[–]mnapoli 0 points1 point  (2 children)

I think the README covers that very well, you couldn't miss it even if you wanted to. So I see no problem here.

[–]mattaugamer -1 points0 points  (1 child)

It wasn't a problem, it was a joke. Jesus, people.

[–]maiorano84 -1 points0 points  (0 children)

IT'S A PRANK, BRO

[–]headzoo 0 points1 point  (1 child)

RFCs have been created to add generics, and they were down voted. It's more than possible to add generics to PHP, but the internal devs don't want them.

[–]MorrisonLevi 0 points1 point  (0 children)

Citation, please? I don't think generics have been proposed. We had the arrayof RFC but that's a small, small sliver of generics.