https://kotlinlang.org logo
Title
v

vaskir

09/27/2018, 9:45 AM
so local functions cannot be recursive?
d

diesieben07

09/27/2018, 9:48 AM
Yes they can, but their declarations are not hoisted, meaning you cannot have a reference to a local function that is declared later.
This is because local functions can reference local variables of the containing function and if you were to call a local function before it's declaration it might have references to local variables of the parent function, which are by that point not even declared...
v

vaskir

09/27/2018, 9:50 AM
hmm. How does it work in all other languages then?..
d

diesieben07

09/27/2018, 9:50 AM
Can you give an example?
v

vaskir

09/27/2018, 9:50 AM
example of what?
a

arekolek

09/27/2018, 9:50 AM
example of other language that has this
v

vaskir

09/27/2018, 9:50 AM
ah. F#
C#
d

diesieben07

09/27/2018, 9:53 AM
How does C# deal with the fact that
Two
can reference local variables declared in
Foo
?
What if you call
One
before the declaration of
Two
?
v

vaskir

09/27/2018, 9:53 AM
I just was about to implement an FSM inside
actor { }
with a bunch of local functions and oops.
d

diesieben07

09/27/2018, 9:54 AM
In Javascript you will get a
ReferenceError
at runtime
Example in JS:
function asd() {
    function one() {
        two()
    }
    one();
    const x = 123;	
    function two() {
        console.log(x);
    }
}
Will produce
Uncaught ReferenceError: x is not defined
.
What does C# do in this case?
v

vaskir

09/27/2018, 9:57 AM
I'm not sure I understand the problem.
Is it possible to declare an outer function local var??
d

diesieben07

09/27/2018, 9:58 AM
You are calling
one
, which calls
two
, which then references
x
. But at the time you are calling it
x
is not even declared or assigned yet
v

vaskir

09/27/2018, 9:58 AM
why it's not declared? I don't think C# executes code line by line 🙂
r

robin

09/27/2018, 9:59 AM
You can work around this by using lambdas instead of actual functions and using lateinit declaration:
fun foo() {
    lateinit var two: () -> Unit
    fun one() {
        two()
    }
    two = {
        
    }
    one()
}
That compiles fine
d

diesieben07

09/27/2018, 9:59 AM
Even if it is declared, it is certainly not assigned
What should it's value be?
a

arekolek

09/27/2018, 10:00 AM
@vaskir can you just translate that javascript code into C# and run it and see what happens? 😉
v

vaskir

09/27/2018, 10:00 AM
this one?
d

diesieben07

09/27/2018, 10:00 AM
No. That is completely different to what I posted.
You can call
One
before your
void Two
declaration, which is the entirety of the problem.
a

arekolek

09/27/2018, 10:01 AM
nope, the one that is said to produce
Uncaught ReferenceError: x is not defined
.
r

robin

09/27/2018, 10:01 AM
@diesieben07 That's not the point though, @vaskir doesn't want to call it earlier, just declare them and use them afterwards
Look at my message to see how that can work in kotlin
d

diesieben07

09/27/2018, 10:02 AM
Yes, I am just explaining why this is not allowed in Kotlin.
Your way with
lateinit
just brings JS' "explode at runtime" behavior to kotlin.
r

robin

09/27/2018, 10:03 AM
Yeah, which is exactly is wanted here.
d

diesieben07

09/27/2018, 10:03 AM
"explode at runtime" should never be wanted 😛
r

robin

09/27/2018, 10:03 AM
So there's never a usecase for using
!!
then?
It definitely is wanted if the language provides no more elegant way to do what you want to do
d

diesieben07

09/27/2018, 10:04 AM
I have written a lot of Kotlin and I've yet to use
!!
outside of quick and dirty experimental code
a

arekolek

09/27/2018, 10:05 AM
use case for
!!
is java interop I think, if you have only kotlin, then I don’t see much use
d

diesieben07

09/27/2018, 10:06 AM
In Java interop you have platform types
You do not need !! there.
r

robin

09/27/2018, 10:07 AM
Please propose a better way of doing what @vaskir is trying to do then
v

vaskir

09/27/2018, 10:08 AM
d

diesieben07

09/27/2018, 10:08 AM
Well, there was only a very limited contrived example posted.
@vaskir What if
x
does not have a constant initializer but is computed? E.g. random number?
It only works because
x
is marked
const
and can be inlined by the compiler.
v

vaskir

09/27/2018, 10:10 AM
it fails
r

robin

09/27/2018, 10:10 AM
@diesieben07 And what if it does? You can simply define all your functions at the top of the closure, and do any code that actually does something afterwards. Yeah, it's not checked by the compiler so it depends on the programmer checking that, but it's simple enough to keep straight. I really don't understand why you you're insisting to bring your example of doing something between the functions into it
v

vaskir

09/27/2018, 10:10 AM
at compile time
a

Andreas Sinz

09/27/2018, 10:10 AM
For now just use regular functions instead of local functions
and submit a feature request
v

vaskir

09/27/2018, 10:12 AM
or use another lang
a

Andreas Sinz

09/27/2018, 10:13 AM
exactly
v

vngantk

12/18/2018, 1:30 AM
Scala allows forward referencing to local functions. It solves the problem mentioned by Take Weiland by imposing some restrictions on how local variables can be positioned. Basically what I understand about the restriction is that a local function, say foo() cannot forward reference another local function, say bar() which references a local variables declared after foo() is declared. This restriction is fair and makes sense, and it does not limit us from a lot of good use cases for local functions.
I really hope Kotlin can support this local function hoisting feature like the way Scala does. It would allow us to write code according to the "stepdown" rule suggested by Uncle Bob in his Clean Code book.
See this thread for discussion about Scala's local function with forward referencing: https://stackoverflow.com/questions/22108579/calling-functions-before-they-are-defined-forward-reference-extends-over-defini