#2403 Returning Void

SlimerDude Wed 11 Mar 2015

Suppose I have this class:

class Example {
    Str doStuff() {
        if (someCondition)
            return moarStuff()
        
        return otherStuff()
    }
    
    Str moarStuff()  { "6" }
    Str otherStuff() { "9" }
}

And I decide doStuff() should return an Int, so I update the class:

class Example {
    Int doStuff() {
        if (someCondition)
            return moarStuff()
        
        return otherStuff()
    }
    
    Int moarStuff()  { 6 }
    Int otherStuff() { 9 }
}

Cool, that was easy, just a minor change to the return types.

But then I realise the return value isn't used at all, so I decide to make doStuff() return Void:

class Example {
    Void doStuff() {
        if (someCondition) {
            moarStuff()
            return
        }
        
        otherStuff()
    }
    
    Void moarStuff()  { /* wotever */ }
    Void otherStuff() { /* wotever */ }
}

Wow! That was a BIG code change! I had to re-jig my if statement, introduce code blocks and delete return keywords! All because I can't return Void.

So, if Void is an object, why can't we return it? (A rhetorical question.)

I'm not asking to be able to create instances of Void and return them, but it would be nice to chain Void return values. That way, in the examples above, changing the return type to Void would be the same as changing it to Int (Well, as far as doStuff() is concerned.)

Above is a small example, but a while ago I did such a change in BedSheet where I was using a chain of command / pipeline pattern and the re-factor ended up being a lot bigger than I wanted.

matthew Wed 11 Mar 2015

Not sure about your specific use case, but I've often thought it would be cool if we could return/assign Void. If we could, we'd be a little closer to making all statements be expressions, which would yield some pretty expressive constructs.

I've frequently wanted to be able to do something like this.

a := if (condition) { 6 } // else Void/null?

val := switch(foo) {
   case 1: "bar"
   case 2: "baz"
   default: null // Void?
}

Your example presumably has a larger ripple effect. If you were returning an Int and now you are returning Void, what about all the code that was assigning the returning value or comparing against it?

This concept would need some fleshing out for how Void relates to null I think, but maybe this is for another thread?

SlimerDude Thu 12 Mar 2015

Hi Matthew,

If you were returning an Int and now you are returning Void, what about all the code that was assigning the returning value or comparing against it?

Yes, the leaf methods that return a value would have to change (as they do in the example) but if the values aren't used (which is why I'm returning Void) then there's nothing comparing it.

In all, changing a return value to Void still requires a lot more effort and code changes than simply changing it to another other object.

When I write:

return otherStuff()

What I am saying is, "Return what otherStuff() is returning". That statement shouldn't care what otherStuff() returns, be it Void or some other object, it's just passing the buck.

I just think it'd be neat if the compiler let you chain together methods that return Void.

Making Void a Real Object

I'm all for making Void a true object. But one with a private ctor that you can't instantiate.

It would make reflection a lot cleaner. Currently Void methods return null... Err?? No! Void methods should return Void instances!

Obviously this would be a big, compatibility breaking, change. An option for Fantom 1.1 maybe?

Void vs Null vs Undefined

What you want is similar to undefined in Javascript which causes a lot of confusion. Granted, that confusion is mainly due to implicit coercion, but I'm still not keen. Given Fantom is compiled and strongly typed - there is no undefined!

In your examples I would just assign a default value first.

a := null
a = if (condition) { 6 }

But yes, statements as expressions can be cool. It also allows you to write some pretty convoluted and evil code!

brian Thu 12 Mar 2015

I actually have been noodling on a small tweak to the language to allow this:

Void foo()
{
  if (x) return bar
}

Void bar() {}

Right now you have to do this:

Void foo()
{
  if (x) { bar; return }
}

That use case seems to come up a lot, so would be nice to fix. I don't really want to take it much further, really just limit to that you can use return any type inside a Void method.

SlimerDude Thu 12 Mar 2015

That would be awesome and is exactly what I'm after!

:D

Yeah, it does come up quite a bit!

elyashiv Fri 13 Mar 2015

I guess changing the semantics to allow a call to a allow something like this:

Void foo()
{
  return bar
}
Void bar() {...}

shouldn't be too complex, and will allow the wanted functionality.

SlimerDude Fri 13 Mar 2015

shouldn't be too complex

Guess we'll just have to wait and see how Brian's noodling comes along!

brian Tue 23 Jun 2015

Ticket promoted to #2403 and assigned to brian

brian Tue 23 Jun 2015

Ticket resolved in 1.0.68

Fix compiler to allow return inside a Void method to optionally include an expression of any type.

So now this is legal Fantom:

Void foo(Int x) 
{ 
  if (x == 0) return bar
  if (x == 1) return baz 
}

Void bar() {}

Str baz() { "baz" }

SlimerDude Tue 23 Jun 2015

Cool. Interesting that Void methods may now return any value. My thinking only went as far as returning Void and keeping the type checking, meaning you'd have to do this:

Void foo(Int x) 
{ 
  if (x == 0) return bar
  if (x == 1) return
}

Void bar() {}

Str baz() { "baz" }

But I can't envision return baz doing any harm, so... sweet!

brian Wed 1 Jul 2015

Yeah its been really nice, used it already a bunch

Jeremy Criquet Wed 22 Jul 2015

Neat. Now we just need Xored to update F4!

Login or Signup to reply.