Re: [Early Feedback] Pattern matching

From: Date: Sat, 22 Jun 2024 21:57:24 +0000
Subject: Re: [Early Feedback] Pattern matching
References: 1 2 3 4 5 6  Groups: php.internals 
Request: Send a blank email to [email protected] to get a copy of this message
On Sat, Jun 22, 2024 at 10:53 PM Rowan Tommins [IMSoP]
<[email protected]> wrote:
>
> On 22/06/2024 19:34, Robert Landers wrote:
>
> I've brought this up before, but I mostly see "as" being useful for
> static analysis. That's what I've mostly used it for C#, anyway.
> Logically, you know the type, but due to one-thing-or-another you
> can't "prove" the type is that type (such as foreach-arrays or dealing
> with results from user-code callbacks in library code). I want to be
> able to say "this is an int or else."
>
>
> I absolutely see the use case for that; I just don't think "as" is a good word
> for it, because that's not what it means in normal English.
>
>
> Incidentally, according to the C# docs at https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast#as-operator
>
> > The as operator explicitly converts the result of an expression to a given reference or
> > nullable value type. If the conversion isn't possible, the as operator returns null. Unlike a
> > cast expression, the as operator never throws an exception.
>
> So it more closely matches my intuition: a statement of just "foo as Bar;" would be
> useless, because it's calculating a value and discarding it, with no side effects.

In general, you assign the result of the operation so that the output
is useful. Here's how that might look in PHP with the C# rules:

function foo(BarInterface $bar) {
  $baz = $bar as Baz;
  $baz?->thing();
  $bar->otherThing();
}

With "is" then it looks a little more wonky but isn't far from the
current instanceof method:

function foo(BarInterface $bar) {
  if ( $bar is Baz ) $bar->thing();
  $bar->otherThing();
}

With fibers/async, "as" is actually more important than "is" (at least
as far as crashing goes):

class Foo {
  public BarInterface $bar;

  public function doStuff() {
    $baz = $this->bar as Baz;
    // some stuff with $baz
    callComplexThing(); // suspends current fiber,
    // $this->bar is no longer the same object
    // or concrete type when we return
    $baz->something();
  }
}

If we were to do an "is" check on the first line, by the time the
fiber is resumed, we've got a completely different type on our hands
and it would crash. Maybe that is desirable, maybe not, but we know
that we have a reference of the type we want and it won't be changed
under us by using "as."

> As you say, the conversion might not be of the value, but of the statically analysed type, but
> in C#, that's all part of the language. In PHP "$foo = $bar as SomeInterface;" would
> have no visible effect except in third-party tooling, where it can already be written "/** @var
> SomeInterface $foo */ $foo = $bar;"

Hopefully my examples show how it can be useful (at least when it
returns null if it is the wrong type). When it gives a TypeError or
something, it becomes far less useful -- at least for the sake of
conciseness. However, it becomes far more useful to dealing with
scalar casts:

function foo(int $type) {}

foo(123.456 as int); // crashes
foo(null as int); // crashes

But even if we did return null, those would crash unless foo() took
int|null, which may or may not be what you want ...

With it always being an error if it doesn't match, it's really not
that useful, as you point out.

>
>
> --
> Rowan Tommins
> [IMSoP]


Thread (79 messages)

« previous php.internals (#123760) next »