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]