> On Sep 6, 2024, at 16:40, Rob Landers <rob@bottled.codes> wrote:
> 
> 
> 
> On Sat, Sep 7, 2024, at 01:34, Larry Garfield wrote:
>> On Fri, Sep 6, 2024, at 6:27 PM, Rob Landers wrote:
>> 
>> >> I suspect there's also other edge case bits to worry about, particularly 
>> >> if trying to combine a complex alias with a complex type, which could 
>> >> lead to violating the DNF rule.  For example:
>> >
>> > Oh, DNF is the bane of my existence with this RFC—I don't want to mess 
>> > this up. I'll see you at the end of the example, though.
>> >
>> >> 
>> >> typealias Foo: (Bar&Baz)|Beep;
>> >> 
>> >> use (Bar&Baz)|Beep as Foo;
>> >> 
>> >> function narf(Foo&Stringable $s) {}
>> >> 
>> >> With the compile time approach, that would expand to 
>> >> `(Bar&Baz)|Beep&Stringable`, which is not a valid type def.
>> >
>> > I can see how you arrived at this, but I think you may have missed a 
>> > step, since the entirety of Foo will be &'d with Stringable.
>> >
>> > Foo = (Bar & Baz) | Beep
>> >
>> > want: (Foo) & Stringable
>> >
>> > expand Foo: ((Bar & Baz) | Beep) & Stringable
>> >
>> > Which can be reduced to the following in proper DNF (at least, it 
>> > compiles—https://3v4l.org/0bMlP):
>> >
>> > (Beep & Stringable) | (Bar & Baz & Stringable)
>> >
>> > It's probably a good idea to update the RFC explaining how expansion works.
>> 
>> Woof.  We're not "fixingup" anyone's DNF elsewhere.  I cannot speak for 
>> everyone, but I'd be perfectly fine not doing any magic fixing for now, and 
>> then debating separately if we should do it more generally.  Just doing it 
>> for aliases doesn't seem like the best plan.
>> 
>> --Larry Garfield
>> 
> 
> Oh, we're definitely not "fixingup" the expression to DNF... more like 
> spending some time in the RFC showing how the expansion is the same execution 
> as with a DNF expression to prove that it is a valid type expression.
> 
> — Rob

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:

function foo(Status $string): void { }

How do I know that Status is a) not a class, b) that I can fulfill the 
requirement with a string, and/or maybe any object with __toString(), or maybe 
it’s ints? Or objects or enums?

Even with file-local aliases (which I would definitely prefer to avoid) we will 
most likely rely on developer tooling (e.g. IDEs and static 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 (which means 
that we can hang methods on to the value) or we need to distinguish between 
type hints for class-likes and type hints for not-class-likes (*Bar anyone?).

Expanding on type-class-likes: within the type methods, $this->value would 
refer to the original value, any operators would follow the _same_ rules as 
either the original values type (e.g. $int = 4; $string  = “foo”; $int . 
$string == “4foo", or call __toString() in all the normal places for strings if 
defined).


type Stringable: string|int {
     public function __toString(): string
     {
          return (string) $this->value; // original value
     }

     // Add Stringable methods here
}.

So, with that in mind… I’d also like to open up the ability for Enums to be 
fulfilled by the backed value, that is:

function foo(Bar $bar): void { }

Where Bar is:

enum Bar: string {
    case BAZ = 'baz';
    case BAT = ‘bat';
}

And you can call `foo()` like: foo(‘baz’) and  Bar::BAZ will be passed in. 

I realize I’m opening a barn down here, but I just don’t see file-local type 
aliases as that useful, and while I like the functionality of type-class-likes, 
I think they would add more non-class behavior (in addition to enums) for 
things that look like classes if we don’t add some sort of identifier. I’d much 
rather that we add backed-value to enum casting, and at least make that more 
consistent with this functionality if we’re going to conflate the syntax.

- Davey

Reply via email to