On Sat, Jun 22, 2024 at 10:53 PM Rowan Tommins [IMSoP]
<imsop....@rwec.co.uk> 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]

Reply via email to