#2145 'As' gives NullErr

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:

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?

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 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_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 on num type and/or expected output type
  • num as? Float - returns Float?
  • num as! Float - returns non-null Float or throws ClassCastException

brian Mon 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

SlimerDude Mon 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#.

andy Thu 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_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.

Login or Signup to reply.