#2272 Famtom implicit narrowing vs other languages

tomcl Tue 29 Apr 2014

This comment is based on my understanding of Fantom - which is imperfect - so it may be completely wrong...

In Java implicit narrowing type conversions are allowed at compile-time when it can be proved that the value narrowed is inside the narrower type. Which is safe, so no run-time checks are needed.

In Fantom implicit narrowing conversions are allowed at compile-time unless it can be proved that the value narrowed is outside the narrower type. Run-time checks are then added.

The burden of proof is opposite.

I had not realised this when first coming to Fantom, but it seems to me to be a radical change and something that colors the whole language.

I'm not complaining about this - I can see the utility. In fact I think one way to look at this is that by migrating some type narrowing checks to run-time it is possible to have a stronger type system and maintain flexibility and convenience (too many explicit casts make code unreadable). An example of this would be the incorporation of nullability.

But it is different from what you might think, which is that as in Java narrowing that might lead to run-time errors must be explicitly cast.

I'm not sure which other statically typed languages have such wholesale implicit type narrowing - normally the contract with static types is that type errors are guaranteed to be caught at compile-time?

The trade-off in Fantom between static and dynamic typing has been mentioned by many, but the fact that it is based on a partial derogation of type checks on implicit narrowing to run-time is not so obvious.

Finally is there any way in Fantom to make the compiler say, as a warning, whenever it is implicitly narrowing types?

SlimerDude Tue 29 Apr 2014

Hi Tom,

I read your words but, err, not being academic I'm struggling to understand! Could you give an example of what you can't do?

tomcl Tue 29 Apr 2014

Int narrow(Num x)
{
  return x
}

So this compiles OK, and inserts a run-time check that x is really an Int.

That is what you'd expect in a dynamic language, but not a static one, where because there may be a type error here the function would not compile.

In fantom you would get a type error for:

Int narrow_bad(Num x)
{
  x = 1.0f  //make sure x is not Int
  return x
}

And as far as I can see from the documentation (and tests) the same thing applies for using any superclass typed object in a context that requires a subclass. Replace Num by Obj in the above example and the method narrow compiles OK, because it is possible Obj might have a value which is an Int.

So the issue is whether this type of narrowing should result in a warning.

Fantom used like this is not the same as a dynamic language because the narrowing will be checked at run-time inside narrow - whereas in a dynamic language no check would be done until the value was used somewhere else.

But it is also not the as a static language where narrow could never compile without an explicit cast because Obj is wider than Int.

SlimerDude Tue 29 Apr 2014

I see.

I guess this would fall under the rubric of Implicit Casts.

To be honest, I'm quite happy to keep it as it is. I don't believe I've ever encountered any problems / runtime errs like the ones you mention.

If I did get a warning, as you suggest, then it probably wouldn't alert me to a problem in my code. (Cos my code is bug free! I wish!) Instead I'd just be forced to add a cast to remove the warning. (Which is what implicit casts do!)

One of the reasons I love my Fantom code over Java, is because I don't have casts and declarations everywhere. The code is less dense.

tomcl Tue 29 Apr 2014

Yes. I'm not saying it is a problem. just a feature that surprised me when I first worked it out.

There is an argument for an option to give compiler warnings whenever this happens since it indicates something that might result in a runtime error. You can always switch such warnings off if you wish not to be distracted.

Interestingly, the typical errors are still caught statically. For example Float -> Int will not compile, because Float and Int are incompatible: there is no value which is both Float and Int. Num works because it is a supertype of Int.

brian Tue 29 Apr 2014

tomcl your writeup of the Fantom's type behavior is spot on!

You are right its opposite of Java which requires an explicit cast for each unknown narrowing operation. But in Fantom we don't have user generics and more importantly we often use -> for dynamic calls, so we don't want to be littering our code with casts.

It was sort of an experiment way back in the day, but its actually has proven itself to work out very well as a good balance b/w type safety and code verbosity.

Login or Signup to reply.