Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:125471 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 1E7041A00BD for ; Sat, 7 Sep 2024 14:57:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1725721148; bh=oHPYOphefUwxAMZgtt4bbpUfAHNtQDombKv29C6v0Og=; h=Date:From:To:In-Reply-To:References:Subject:From; b=hbhX4TURVMotptHbkOoOimdp+aVA5u5Y4OJPb+P/HT30+XmEehXnii8qOkQ0x7vrJ Mcc+Kqp1jGXm9XR2GX5VTuBQkEep+lO31ApQyGXbo6DfnlwcQxVT6iC0chBAc+MNQ7 INUwX6cQ0FZIQjFOfzr+XXXvt97CEtxnS0Y44ZO1EaDtanz4fhSnQ3ByLwzmTl55eu wTvnoZSnbbYyJptlNOG+LR8z7yZ7jyKRfHUx01GIczenXTibSLS0bWd3Sy2sMKpZVq 27GCYhzZTRNnO9pmNjvm7Jc+98ZK6FWQeO2oZuK6De4w9H38eXZoX0ahrClCXORVYZ 9bi0/6HSZ+Xbw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 2CA2C180051 for ; Sat, 7 Sep 2024 14:59:07 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from fout2-smtp.messagingengine.com (fout2-smtp.messagingengine.com [103.168.172.145]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sat, 7 Sep 2024 14:59:06 +0000 (UTC) Received: from phl-compute-01.internal (phl-compute-01.phl.internal [10.202.2.41]) by mailfout.phl.internal (Postfix) with ESMTP id DD1391380251 for ; Sat, 7 Sep 2024 10:57:06 -0400 (EDT) Received: from phl-imap-09 ([10.202.2.99]) by phl-compute-01.internal (MEProxy); Sat, 07 Sep 2024 10:57:06 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm2; t=1725721026; x=1725807426; bh=0IimuwcKyg nr0Wih4gkLCMy+ifkoSErd0r2JnA78jwU=; b=PY4Zy9E/kC7jaEKSkXmxtBL67K 2e5YToiuX5V3rFOyBNvsWaqOm9w7cOur6ScXoyDoXBOQWBZsHHe5sGvpOkl47HZq MMcXsKe/5gjRMIDup9/t0EOj8pfa+MCdhvuuLu08DBUTAtIXzSJzAp8WYP8hQZuL T3g1LVtosf5WBc/vfF7ZBsL24zeXxyHgR3pnYfhG9/kWE8IPamIrpffmWCO7QxrX WRTKdIYNnMu3K67zUvL8uPcKXnlc7/kYBrWGIrtVoQjuFpnJy4fQ631+keKZHlRU tqd0ZOEzH1LS5rC8iV3IwhRIpH8Cn/34Pi5Agb/3xfVGJrXdYQVqLx46bUqA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm1; t=1725721026; x=1725807426; bh=0IimuwcKygnr0Wih4gkLCMy+ifko SErd0r2JnA78jwU=; b=qvPvv68ufpQOE6s23HRBcoSqmJ4LBl8vS8L693C0t5XD n2EVl9sn601s5urwWf6lhxSZiCcuYFh3rDQPM59fzg48xh3vxK8UY4iUev+0DL+/ 9foBue8BrvroHtWnXcA940wsIFnv0yHQ7cYQDnlxGu1OoSRioaOSrDACIUCa0UyJ T4YP/em8uTsBuVQw+cbkMsdvFdOQ/QrCUYB/SNIa3/7V+9B/V8E6QDADWp7EmvgL u4ipMLklLWsXFxSw/kMSXgfHrctYgXI0zo4kaH7Rp4VLpPg9/TX4GxjCjRfhDS6Q Yg15b8LS4Vbm20y5tTwkIOV+A1RRmRHEp8StHJOCwg== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeftddrudeifedgkeehucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggvpdfu rfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefoggffhf fvkfgjfhfutgesrgdtreerredtjeenucfhrhhomhepfdftohgsucfnrghnuggvrhhsfdcu oehrohgssegsohhtthhlvggurdgtohguvghsqeenucggtffrrghtthgvrhhnpedtueejtd ethfeulefhtdelieduteelffdtudelheffgedtieehhfelieejgfevgeenucevlhhushht vghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehrohgssegsohhtthhlvg gurdgtohguvghspdhnsggprhgtphhtthhopedupdhmohguvgepshhmthhpohhuthdprhgt phhtthhopehinhhtvghrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 96DB5780067; Sat, 7 Sep 2024 10:57:06 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 Date: Sat, 07 Sep 2024 16:56:46 +0200 To: internals@lists.php.net Message-ID: <37baa8a0-dfc1-4df3-97dc-64cceadd4d97@app.fastmail.com> In-Reply-To: <0d461700-1b6c-44fd-9cda-aa698de49847@app.fastmail.com> References: <0fa39535-f22d-4eba-b4df-90abe39e683a@app.fastmail.com> <79e58673-50ec-461e-a998-736b020e4287@app.fastmail.com> <928A2984-6035-4DA6-9EA7-12E85237C270@php.net> <0d461700-1b6c-44fd-9cda-aa698de49847@app.fastmail.com> Subject: Re: [PHP-DEV] bikeshed: Typed Aliases Content-Type: multipart/alternative; boundary=493273da84a24ea1a94e921804a60bfb From: rob@bottled.codes ("Rob Landers") --493273da84a24ea1a94e921804a60bfb Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Sat, Sep 7, 2024, at 15:28, Larry Garfield wrote: > On Fri, Sep 6, 2024, at 7:46 PM, Davey Shafik wrote: >=20 > > My main struggle with this is readability. As much as I want custom=20 > > types (and type aliases is a good chunk of the way there), the main=20 > > issue I have is understanding what the valid inputs are: > > > > function foo(Status $string): void { } > > > > How do I know that Status is a) not a class, b) that I can fulfill t= he=20 > > requirement with a string, and/or maybe any object with __toString()= ,=20 > > or maybe it=E2=80=99s ints? Or objects or enums? > > > > Even with file-local aliases (which I would definitely prefer to avo= id)=20 > > we will most likely rely on developer tooling (e.g. IDEs and static=20 > > analyzers) to inform the developer what the right input types are. > > > > I would very much prefer to either go all in with an Enum-like (whic= h=20 > > means that we can hang methods on to the value) or we need to=20 > > distinguish between type hints for class-likes and type hints for=20 > > not-class-likes (*Bar anyone?). > > > > Expanding on type-class-likes: within the type methods, $this->value=20 > > would refer to the original value, any operators would follow the=20 > > _same_ rules as either the original values type (e.g. $int =3D 4; $s= tring=20 > > =3D =E2=80=9Cfoo=E2=80=9D; $int . $string =3D=3D =E2=80=9C4foo", or= call __toString() in all the=20 > > normal places for strings if defined). > > > > > > type Stringable: string|int { > > public function __toString(): string > > { > > return (string) $this->value; // original value > > } > > > > // Add Stringable methods here > > }. >=20 > Methods on typedefs was the sort of "other stuff classes do" that I wa= s trying to avoid getting into right now. :-) Mainly because I can tota= lly see how it's tempting, but also have no idea what it would mean from= a type-theoretic perspective. It would only make sense if we're talkin= g type DEFs, not type ALIASes. I'm not against that, and it could be fu= n to try and think through the type theoretical implications, but I don'= t think that's what Rob was going for so I didn't want to take that side= quest just yet. (Though if he's OK with it, I'm OK with it.) To be fair, I should probably mention that I've already explored it some= (which I alluded to in another thread a couple of weeks ago). =F0=9F=98= =89 So... I guess it is 'on-topic' for no other reason than it is intere= sting. Here are my notes thus far: __Sugary Generics__ Since we could attach behaviors (at least at the engine level) we could = use this to implement generics. Imagine we have a Box and want to ins= tantiate a Box. To do this, when we define a Box, we actua= lly define an alias internally. This is what the definition of our generic box class in PHP might look l= ike: class Box { public T $var; } It would get compiled to something like this (though it would probably b= e invalid php, it perhaps illustrates my meaning): class Box { public BoxT $var; public function __construct(private alias BoxT) {} } Then, to instantiate a Box, the engine compiles the construc= tor, passing the types as arguments (still probably not ever valid php) = and stealing a bit from python's "self": $box =3D new Box; // compiles to $box =3D new Box(alias int|f= loat); The beauty of this is that it automatically becomes an error if you forg= et the type argument (and if we made it the last argument, would allow s= etting default values for BC reasons like Collection) Even constraints on T could be expressed similarly: class Box { public T $var; } which may get compiled into this not ever valid php: class Box { public BoxT $var; private alias BoxTConstraint =3D> int|float; public function __construct(private alias BoxT) { if (BoxT is not a BoxTConstraint) throw new RunTimeException(); } Basically, it just needs to check that the BoxT alias is of the right ty= pe during construction, essentially making generics just a sugary layer = over aliases. __Ambitious Type System Replacement__ I've also explored it in the case of types, in general, by replacing the= entire type system with this way of representing types (where a special= class represents a real type of value and its behavior). This would all= ow for defining casting rules, operators, passing types as values (for p= attern matching), etc on types themselves. I have no idea what that woul= d look like "at scale", but it is interesting to think about because pri= mitive types would have the same way of working as classes and everythin= g else. It would also separate types from their implementation=E2=80=94w= hether we want to expose this to user-land is a different story.=20 I suspect this could be a 'technical-only' change and not affect user-la= nd at all. zvals would probably get a lot simpler though...=20 I generally stop myself from thinking too much about it, because while i= nteresting, it is a TON of work. Not that I'm afraid of doing that work,= I just don't want to do it by myself. So, I'm cautiously optimistic as = a >=3D9.0 type thing and finding the right people/support. I have no ide= a how to do that, but I can at least try. __Constraints__ Another exploration is that it would potentially allow for setting some = constraints on raw values: alias EmailAddress =3D> string { if (!is_valid_email($value)) throw new RuntimeException('not a valid e= mail address'); } However, I think there is a better solution for that (classes/structs/re= cords/etc)... except for __Typed Literals__ This could easily allow typed literals for value types. After all, a typ= ed literal can be expressed as an alias over a type with a constraint. __The Hammer and Screw Problem__ There are probably other use-cases by making the type-system more 'class= -like', but I will say that PHP's class-system is quite robust=E2=80=94i= f not as robust as its arrays=E2=80=94and should probably be relied on m= ore. That being said, I've been working through this for a bit now, and = it seems that I might be wielding this as a hammer and seeing nails ever= ywhere, even when they are screws. So, while interesting, in many cases,= it probably isn't the best way to do things. =E2=80=94 Rob --493273da84a24ea1a94e921804a60bfb Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable

=
On Sat, Sep 7, 2024, at 15:28, Larry Garfield wrote:
<= /div>
On Fri, Sep 6, = 2024, at 7:46 PM, Davey Shafik wrote:

> = My main struggle with this is readability. As much as I want custom = ;
> types (and type aliases is a good chunk of the way = there), the main 
> issue I have is understanding = what the valid inputs are:
>
> functio= n foo(Status $string): void { }
>
> Ho= w do I know that Status is a) not a class, b) that I can fulfill the&nbs= p;
> requirement with a string, and/or maybe any object= with __toString(), 
> or maybe it=E2=80=99s ints?= Or objects or enums?
>
> Even with fi= le-local aliases (which I would definitely prefer to avoid) 
> we will most likely rely on developer tooling (e.g. IDEs an= d static 
> analyzers) to inform the developer wha= t the right input types are.
>
> I wou= ld very much prefer to either go all in with an Enum-like (which 
> means that we can hang methods on to the value) or we = need to 
> distinguish between type hints for clas= s-likes and type hints for 
> not-class-likes (*Ba= r anyone?).
>
> Expanding on type-clas= s-likes: within the type methods, $this->value 
&g= t; would refer to the original value, any operators would follow the&nbs= p;
> _same_ rules as either the original values type (e= .g. $int =3D 4; $string 
>  =3D =E2=80=9Cfoo=E2= =80=9D; $int . $string =3D=3D =E2=80=9C4foo", or call __toString() in al= l the 
> normal places for strings if defined).
>
>
> type Stringable: = string|int {
>      public fun= ction __toString(): string
>    &nb= sp; {
>        =    return (string) $this->value; // original value
>      }
>
<= div>>      // Add Stringable methods here
> }.

Methods on typedefs was= the sort of "other stuff classes do" that I was trying to avoid getting= into right now. :-)  Mainly because I can totally see how it's tem= pting, but also have no idea what it would mean from a type-theoretic pe= rspective.  It would only make sense if we're talking type DEFs, no= t type ALIASes.  I'm not against that, and it could be fun to try a= nd think through the type theoretical implications, but I don't think th= at's what Rob was going for so I didn't want to take that side quest jus= t yet.  (Though if he's OK with it, I'm OK with it.)

To be fair, I should probably mention that I= 've already explored it some (which I alluded to in another thread a cou= ple of weeks ago). =F0=9F=98=89 So... I guess it is 'on-topic' for no ot= her reason than it is interesting.

Here are= my notes thus far:

__Sugary Generics__

Since we could attach behaviors (at least at the engin= e level) we could use this to implement generics. Imagine we have a Box&= lt;T> and want to instantiate a Box<int|float>. To do this, whe= n we define a Box<T>, we actually define an alias internally.
<= /div>

This is what the definition of our generic box = class in PHP might look like:

class Box<= T> { public T $var; }

It would get compi= led to something like this (though it would probably be invalid php, it = perhaps illustrates my meaning):

class Box = {
  public BoxT $var;
  public fun= ction __construct(private alias BoxT) {}
}

<= /div>
Then, to instantiate a Box<int|float >, the engine compi= les the constructor, passing the types as arguments (still probably not = ever valid php) and stealing a bit from python's "self":
<= br>
$box =3D new Box<int|float>; // compiles to $box =3D= new Box(alias int|float);

The beauty of th= is is that it automatically becomes an error if you forget the type argu= ment (and if we made it the last argument, would allow setting default v= alues for BC reasons like Collection<T =3D mixed>)

<= /div>
Even constraints on T could be expressed similarly:
<= div>
class Box<T: int|float> { public T $var; }
<= /div>

which may get compiled into this not ever valid= php:

class Box {
  publ= ic BoxT $var;
  private alias BoxTConstraint =3D> = int|float;
  public function __construct(private alias Bo= xT) {
    if (BoxT is not a BoxTConstraint) thro= w new RunTimeException();
}

Basic= ally, it just needs to check that the BoxT alias is of the right type du= ring construction, essentially making generics just a sugary layer over = aliases.

__Ambitious Type System Replacemen= t__

I've also explored it in the case of types,= in general, by replacing the entire type system with this way of repres= enting types (where a special class represents a real type of value and = its behavior). This would allow for defining casting rules, operators, p= assing types as values (for pattern matching), etc on types themselves. = I have no idea what that would look like "at scale", but it is interesti= ng to think about because primitive types would have the same way of wor= king as classes and everything else. It would also separate types from t= heir implementation=E2=80=94whether we want to expose this to user-land = is a different story.

I suspect this could= be a 'technical-only' change and not affect user-land at all. zvals wou= ld probably get a lot simpler though...

I = generally stop myself from thinking too much about it, because while int= eresting, it is a TON of work. Not that I'm afraid of doing that work, I= just don't want to do it by myself. So, I'm cautiously optimistic as a = >=3D9.0 type thing and finding the right people/support. I have no id= ea how to do that, but I can at least try.

= __Constraints__

Another exploration is that it = would potentially allow for setting some constraints on raw values:
<= /div>

alias EmailAddress =3D> string {
  if (!is_valid_email($value)) throw new RuntimeException('not a= valid email address');
}

However= , I think there is a better solution for that (classes/structs/records/e= tc)... except for

__Typed Literals__

This could easily allow typed literals for v= alue types. After all, a typed literal can be expressed as an alias over= a type with a constraint.

__The Hammer and= Screw Problem__

There are probably other u= se-cases by making the type-system more 'class-like', but I will say tha= t PHP's class-system is quite robust=E2=80=94if not as robust as its arr= ays=E2=80=94and should probably be relied on more. That being said, I've= been working through this for a bit now, and it seems that I might be w= ielding this as a hammer and seeing nails everywhere, even when they are= screws. So, while interesting, in many cases, it probably isn't the bes= t way to do things.

=E2=80=94 Rob
--493273da84a24ea1a94e921804a60bfb--