I've been a fan of using the as keyword because it produces neater code and allows me ditch the type declaration. And while grabbing objects from afIoc in test code I use it a lot.
Then this happened:
class AsBug {
static Void main(Str[] args) {
num := num as Float
num.toStr
}
static Num num() { 69 }
}
sys::NullErr: java.lang.NullPointerException
Wotever::AsBug.main (AsBug.fan:5)
java.lang.reflect.Method.invoke (Method.java:597)
...
Obviously I would have expected a CastErr.
If this a bug, or am I mis-understanding / abusing the as operator?
I ask because this NullErr took me some time to track down, for I believed my functions to be returning null somewhere - which of course they weren't. If this is by design, how can we prevent other newbies from falling foul to it?
KevinKelleySun 19 May 2013
I think it's by design, but it bothers me too. The as operator yields a nullable type, so it's like
Float? num := num as Float
which is kind of counterintuitive, you're saying you want it as a Float but you get a Float?.
I think this discussion's been around before, but I'd like to see it get revisited, it's kind of a hole in the type system, it seems to me. You write code that you think is, by design, null-safe, but this lets the nulls back in.
I guess, the . operator could give an error when applied to a nullable object (at your num.toStr line), requiring you to either use a hard cast or a test, or use the ?. call...
alex_panchenkoMon 20 May 2013
There are good use cases for having both, so adding some more flexibility makes sense.
e.g.
num as Float - infer nullability based on num type and/or expected output type
num as? Float - returns Float?
num as! Float - returns non-null Float or throws ClassCastException
brianMon 20 May 2013
The as operator works exactly like C# which is:
num is Float ? (Float)num : null
Technically you could force every as statement type's to end with ? since that would be most correct - but it would be really annoying, not to mention a huge backward breaking change
SlimerDudeMon 20 May 2013
So that's the bit I didn't understand - I would have expected:
num is Float ? (Float) num : throw CastErr()
Because nowhere in the example code do I produce a null - I was like, "Where is it coming from?"
I think the conclusion is, those who don't use C# (myself included), expect the as operator to behave as a drop in replacement for casting - which it clearly isn't.
Out of interest, does anyone actually use this nulability feature? Where they expect as to return null if the type doesn't match? Myself, I can't see as such as common usage that it ever warranted it's own keyword!? Silly C#.
andyThu 23 May 2013
expect the as operator to behave as a drop in replacement for casting - which it clearly isn't.
Wouldn't make sense to have two different operators that did the same thing ;)
expect the as operator to behave as a drop in replacement for casting - which it clearly isn't.
The as operator is used where you expect the value may not be the specified type, so it returns null to make it easy to work with that condition. Some of the dynamic features of Fantom make this less useful than in C# - but I do still use this feature - makes for cleaner code.
But - if you know the object must be that type, you should always use a cast. A CastErr is alot easier to debug than a NPE.
alex_panchenkoFri 24 May 2013
It make sense to disclose the normal cast also in the docLang::TypeSystem page (there are implicit casts there, but not normal), at the moment it is mentioned only in docLang::Expressions - Type Checking.
SlimerDude Sun 19 May 2013
I've been a fan of using the
as
keyword because it produces neater code and allows me ditch the type declaration. And while grabbing objects from afIoc in test code I use it a lot.Then this happened:
Obviously I would have expected a
CastErr
.If this a bug, or am I mis-understanding / abusing the
as
operator?I ask because this
NullErr
took me some time to track down, for I believed my functions to be returningnull
somewhere - which of course they weren't. If this is by design, how can we prevent other newbies from falling foul to it?KevinKelley Sun 19 May 2013
I think it's by design, but it bothers me too. The
as
operator yields a nullable type, so it's likewhich is kind of counterintuitive, you're saying you want it as a
Float
but you get aFloat?
.I think this discussion's been around before, but I'd like to see it get revisited, it's kind of a hole in the type system, it seems to me. You write code that you think is, by design, null-safe, but this lets the nulls back in.
I guess, the
.
operator could give an error when applied to a nullable object (at yournum.toStr
line), requiring you to either use a hard cast or a test, or use the?.
call...alex_panchenko Mon 20 May 2013
There are good use cases for having both, so adding some more flexibility makes sense.
e.g.
num as Float
- infer nullability based onnum
type and/or expected output typenum as? Float
- returns Float?num as! Float
- returns non-null Float or throws ClassCastExceptionbrian Mon 20 May 2013
The
as
operator works exactly like C# which is:Technically you could force every
as
statement type's to end with?
since that would be most correct - but it would be really annoying, not to mention a huge backward breaking changeSlimerDude Mon 20 May 2013
So that's the bit I didn't understand - I would have expected:
Because nowhere in the example code do I produce a
null
- I was like, "Where is it coming from?"I think the conclusion is, those who don't use C# (myself included), expect the
as
operator to behave as a drop in replacement for casting - which it clearly isn't.Out of interest, does anyone actually use this nulability feature? Where they expect as to return null if the type doesn't match? Myself, I can't see as such as common usage that it ever warranted it's own keyword!? Silly C#.
andy Thu 23 May 2013
Wouldn't make sense to have two different operators that did the same thing ;)
The
as
operator is used where you expect the value may not be the specified type, so it returnsnull
to make it easy to work with that condition. Some of the dynamic features of Fantom make this less useful than in C# - but I do still use this feature - makes for cleaner code.But - if you know the object must be that type, you should always use a cast. A CastErr is alot easier to debug than a NPE.
alex_panchenko Fri 24 May 2013
It make sense to disclose the normal cast also in the docLang::TypeSystem page (there are implicit casts there, but not normal), at the moment it is mentioned only in docLang::Expressions - Type Checking.