#2509 Implicit Function Return Types

SlimerDude Sun 7 Feb 2016

I'll start by saying programmers are lazy. Often this is a good thing, as you end up automating some tasks and simplifying others. It also means we want to type less, which is where type inference and implicit casts come in. It's one of the many reasons why Fantom is a joy to program. It's also why I proposed the enhancement to it-blocks.

Whilst trying other ways to reduce my typing I noticed something scary with List.map(). It has the following signature:

Obj?[] map(|V item, Int index->Obj?| c)

But note the output from these valid incantations:

list := [1, 2, 3]

list.map                  { it.toStr }          // --> [1, 2, 3]
list.map |int|            { int.toStr }         // --> [1, 2, 3]
list.map |Int int|        { int.toStr }         // --> [null, null, null]
list.map |int|            { return int.toStr }  // --> [1, 2, 3]
list.map |Int int|        { return int.toStr }  // --> [null, null, null] (Fantom 1.0.68 only)
list.map |Int int -> Str| { int.toStr }         // --> [1, 2, 3]
list.map |Int int -> Str| { return int.toStr }  // --> [1, 2, 3]

I would have expected [1, 2, 3] to be returned from all the calls.

I know that technically the signature |Int int| defines a Void function, but it's a single statement with an (implicit) return value. Further more, it's being handed to a method that expects a function with a return value, not a Void func.

Could the function's return type be coerced to what the method signature expects? I can't think of a scenario where this would not be wanted.

(Side note: I've mentioned this before, but I believe a lot of the knarly messiness around reflection stems from Void being considered the same as null whereas really they should be 2 very different objects. Shame we're stuck to handling what Java and other platforms give us.)

andy Mon 8 Feb 2016

Yeah thats bitten me before a few times - might be nice - but not sure I've run into it enough to change personally.

brian Mon 8 Feb 2016

I agree its non-ideal, but sort of have to pick the lesser of various evils. Right now introducing a hard type into a closure signature means its explicit typed. There isn't any notion of "mixed type inference" were you let compiler infer some parameter/return types and you explicitly type others. When the return type is not void, that actually makes the best sense. One thing we could evaluate is saying that a function map which expects a return Obj like that doesn't allow Void

brian Wed 10 Feb 2016

Ticket promoted to #2509 and assigned to brian

Still thinking about this, but I think at the very least the existing behavior is incorrect - even if we just need to add a compile time error

SlimerDude Tue 11 Jul 2023

Hi, I'm just bumping this as it still bites us today.

There isn't any notion of "mixed type inference" were you let compiler infer some parameter/return types and you explicitly type others.

How then, does that differ from when we declare:

["foo", "bar"].map |Str val, index| { ... }

because the compiler instinctively knows that index is an Int. Could it also know (and enforce) that the func should return a value?

Coolrasta Tue 18 Jul 2023

Hi SlimerDude

With regards to the difference in output when specifying the return type, it seems to be tied to the way Fantom handles type inference and implicit return. As you've rightly pointed out, when the return type is explicitly stated (|Int int -> Str|), the function works as expected. However, when the return type is not mentioned, it defaults to Void, despite the fact that the function does have an implicit return.

The idea of coercing the function's return type to what the method signature expects is interesting. However, it could potentially introduce a layer of complexity in certain edge cases. I believe this design choice was made in Fantom to allow for flexibility and ensure type safety.

About your side note, I understand your concerns about how Void is considered the same as null. The distinction could indeed create some semantic clarity. However, as you mentioned, the practical implications of such a change, especially given Fantom's interoperability with Java and other platforms, could be quite complex.

Login or Signup to reply.