It seems to me you are shooting while blindfolded.

> I changed you code `let result = closure(n)` to `let result = closure(1)`. I 
> thought that should eliminate the error. It didn't. The compiler asked me to 
> change the whole code as below:


Why did you expect changing which argument to send the closure to make any 
difference?

> func mainFunction(closure: @escaping (Int) -> Int) -> Int {


Sure declaring the closure as escaping shuts up the compiler. At the cost of 
having an escaping closure in a context where the closure is guaranteed no to 
escape. My understanding of the distinction between escaping and non escaping 
is it enables compiler optimisations (in the non escaping case) that would not 
be possible in the general case. Therefore this compiler-mandated change kills 
performance for nothing


> So I think in `func closureDoubled(_ n: Int) -> Int`, the `closure` is 
> escaping other than non-escaping.

No. The closure is not escaping. At least as far as I can see. Can you point 
out where it is escaping? Declaring a closure as escaping does not make it so. 
It only *allows* it to escape.

Note that here, “escaping” means escaping the scope where it is declared, i.e. 
the `mainFunction` function. Clearly, it does not.


>  The result is not calculated inside the function `closureDoubled`, it was 
> calculated at `return temp1+temp2`, that is what I think can explain the 
> behavior. 


Where does this speculation comes from? It totally contradicts the story that 
the source code tells. temp1 and temp2 and Int variables that are local to 
`mainFunction `, while `result` is local to the nested function 
`closureDoubled`. Unless you use of the word “result” does not refer to the 
`result` variable. I apologise for the poor choice of variable names that made 
possible this confusion.

> I don't know why at first. It just like there were two ways to do the job, 
> you thought it worked in this way, but it chose the other way. Both ways lead 
> to the same result.

You lost me in that sentence. What do the pronouns refer to?

> Then I changed your code to 

yes you totally changed my code by adding a closure parameter to the nested 
function, in which you passed the closure that was passed to the outer function.

By doing so, however, you altered significantly my use case. This is because 
the point of having a nested function, as opposed to a separate function 
defined outside the outer function, is for the nested function to capture the 
environment of its containing function.

Of course, this begs the question: this is semantically equivalent, so why 
objecting to that? There are two answers to that question:

1- preventing a nested function from capturing a local closure is a significant 
limitation which essentially demotes closure from the status of first-class 
citizen. The artificial limitation of expressiveness in the language is 
unwarranted and a serious flaw (if intended. I still believe this is a bug)
2- A very common pattern for nested functions is to make recursive algorithms 
more memory-efficient by forwarding the recursion to a nested function whose 
parameters are only those values that change during the recursion. Other 
values, such as the parameter you just added in your change, if passed as 
parameters, will only waste stack space since they will never change in the 
recursion. This will also degrade performance slightly because the code will 
have to push this unchanging value to the stack every time a recursive call is 
made.

Note that this was the context where I was bitten by the problem. Of course, 
you could expect a smart compiler to recognise that is doesn’t have to push 
unchanging parameter values onto the stack in recursive calls. Similarly, there 
are smart compiler which recognise terminal recursion and transform recursion 
into iteration. I even encountered in the past (Lisp) compilers which 
automatically transformed simple cases of non-terminal recursion into 
terminal-recursive versions (by adding an accumulator parameter), followed by 
transformation into iterative code. I do not expect typical compilers to do 
that (where “typical” is left undefined, but would include GCC, Clang, Swift). 
This is why I tend to implement my recursive calls in the way just described.

> Everything works as expected now.

Expected, perhaps. As intended, certainly not.

> So I think the reason is just because of `closure` was not define in `func 
> closureDoubled` in the first code snippet. It was defined outside, so it was 
> escaping. What do you think?

I think this explanation does not make sense to me. Maybe I am missing 
something. Could you possibly detail what you mean?

Thank you very much for your attempts. It’s possible something is escaping me 
(pun intended). But I still believe this is a bug in the compiler, if not in 
the language.

Best regards.

Jean-Denis




> On 10 Oct 2016, at 02:45, Zhao Xin <owe...@gmail.com> wrote:
> 
> I changed you code `let result = closure(n)` to `let result = closure(1)`. I 
> thought that should eliminate the error. It didn't. The compiler asked me to 
> change the whole code as below:
> 
> func mainFunction(closure: @escaping (Int) -> Int) -> Int {
>     
>     func closureDoubled(_ n: Int) -> Int {
>         let result = closure(1)
>         return 2*result
>     }
>     
>     let temp1 = closure(1)
>     let temp2 = closureDoubled(1)
>     return temp1+temp2
> }
> 
> So I think in `func closureDoubled(_ n: Int) -> Int`, the `closure` is 
> escaping other than non-escaping. The result is not calculated inside the 
> function `closureDoubled`, it was calculated at `return temp1+temp2`, that is 
> what I think can explain the behavior. 
> 
> I don't know why at first. It just like there were two ways to do the job, 
> you thought it worked in this way, but it chose the other way. Both ways lead 
> to the same result.
> 
> Then I changed your code to 
> 
> func mainFunction2(closure:  (Int) -> Int) -> Int {
>     
>     func closureDoubled(_ n: Int, closure2:(Int) -> Int) -> Int {
>         let result = closure2(1)
>         return 2*result
>     }
>     
>     let temp1 = closure(1)
>     let temp2 = closureDoubled(1, closure2: closure)
>     return temp1+temp2
> }
> 
> Everything works as expected now. So I think the reason is just because of 
> `closure` was not define in `func closureDoubled` in the first code snippet. 
> It was defined outside, so it was escaping. What do you think?
> 
> Zhaoxin
> 
> On Mon, Oct 10, 2016 at 8:07 AM, Jean-Denis Muys via swift-users 
> <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:
> Here is a contrived reduction of my problem
> 
> func mainFunction(closure:(Int) -> Int) -> Int {
> 
>     func closureDoubled(_ n: Int) -> Int {
>         let result = closure(n)
>         return 2*result
>     }
> 
>     let temp1 = closure(1)
>     let temp2 = closureDoubled(1)
>     return temp1+temp2
> }
> 
> The line "let result = closure(n)" is refused by the compiler with the error 
> message "declaration over non closing parameter may allow it to escape".
> 
> First off, while I know what an escaping or a non-escaping closure is, I find 
> this error message utterly impossible to understand. To begin with, the 
> sentence "non closing parameter" is meaningless to me.
> 
> In any case, my main function is passed a non-escaping closure. I want to 
> call it from inside it, the compiler is ok with. I want also to call it from 
> a nested function, but the compiler disagrees.
> 
> I believe the compiler should not complain here. Did I miss anything?
> 
> Jean-Denis
> 
> 
> 
> 
> _______________________________________________
> swift-users mailing list
> swift-users@swift.org <mailto:swift-users@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-users 
> <https://lists.swift.org/mailman/listinfo/swift-users>
> 
> 

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to