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:
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.)
andyMon 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.
brianMon 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
brianWed 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
SlimerDudeTue 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?
CoolrastaTue 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.
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:But note the output from these valid incantations:
I would have expected
[1, 2, 3]
to be returned from all the calls.I know that technically the signature
|Int int|
defines aVoid
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 aVoid
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 asnull
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.
How then, does that differ from when we declare:
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.