On Sat, Jun 22, 2024 at 11:57 PM Robert Landers
<landers.rob...@gmail.com> wrote:
>
> 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.

Side-note: this is why my original proposal had two modes (based on
the existence of null in the typecheck):

foo(123.456 as int);

would crash due to being unable to cleanly cast to int.

foo(123.456 as int|null);

would crash from passing null (since literally everything can be
casted to null and it can't be casted to an int).

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

Reply via email to