#2621 Closure Return Type Inference

go4 Thu 27 Jul 2017

I have a bug at the following code:

|Str->Obj| func := |v| { return "Hi" }
echo(func(""))

It allow an explicit return keyword, but the return type can't be inferred.

Thanks.

brian Wed 23 Aug 2017

As a general rule, the RHS (right hand side) is never inferred from the LHS (left hand side), but rather vise versa. So your function is typed as returning Void.

Then separately we made a change a while back to allow returning a value from Void methods so that you can write code like this:

Void bar()
{
  if (true) return foo
  echo("not run") 
} 

Obj? foo() { null }

So that is a case where the new behavior removes what would have been a useful error message

go4 Wed 23 Aug 2017

Thank you Brian.

So think about change Obj to Str:

Void bar()
{
  if (true) return foo
  echo("not run") 
} 

Str? foo() { null }

It's also works. But it breaks the type compatibility rules to cast from Str to Void: Subtype Substitution and Type Compatibility

The purpose of original post is returning Void (not anything) from Void methods.

brian Thu 24 Aug 2017

Void is kind of a special case, and with that change in 2015 we basically allowed any return expression inside of Void as "ignore return expression". Given that change has been out in the wild for several years now, its a little too late to undo. Personally I've found it extremely useful in short circuiting code, despite the fact it allows some things to slip thru the compiler - its a trade-off like everything

go4 Thu 24 Aug 2017

Another view is inferred from lambda body not LHS. This is also most other lambda(not closure) base language do.

SlimerDude Tue 12 Sep 2017

Just to say that, I too, really don't like the way any object can be returned from a Void method.

Apart from just "feeling wrong", the original idea was to treat Void in a similar way to other objects, and let Void results propagate up call stack.

Given that change has been out in the wild for several years now, its a little too late to undo.

I don't see how. If the patch were to work as intended, then all the short circuits would still work:

// example 1
Void myMethod() {
    return echo("")
}

The only bits of source code that would need re-compiling, would be the nonsense bits that shouldn't exist, such as:

// example 2
Void myMethod() {
    return 13
}

I've found it extremely useful in short circuiting code

For the cases where Void results are propagated, me too.

But as I often use a container to call methods via reflection, there are many times where the unwanted behaviour causes problems. Mainly when I decide to change a Void method to return a value. I'll update all the return statements, but forget to update the method signature. So I have code similar to example 2 above and wonder why nothing is being returned!

brian Wed 4 Oct 2017

You can definitely make a case that the trade-off that the convenience for returning anything from Void isn't worth the possible errors you can detect at compile time. But at this point, the behavior has been that way for two years. I double checked our code base and there is 100s of places where we do this. So its too late to reverse our decision. When I went back to double check, places where I'm noticing it used are often with things like a writing to an output stream which always returns This. So even though I have been caught by that behavior on occassion, I actually think its still the right trade-off

Login or Signup to reply.