3

Alright, so I have two recursion functions with the exact same purpose:

let Fac = { n -> n == 0 ? 1 : n * Fac(n - 1) }

function! Fact(n)
    return a:n == 0 ? 1 : a:n * Fact(a:n - 1)
endfunction

While Fact works fine, recursive use of Fac seems to confuse vim:

:echo Fac(10)
Error detected while processing function <lambda>20:                                                                                  
line    1:
E117: Unknown function: Fac
-1

Does lambda expression not support recursion, or did I call Fac in an incorrect way?

2 Answers 2

3

It is possible, but you must refer to Fac as a "global" with g: to allow the lambda to refer to "itself," even if it is not actually defined already.

let Fac = { n -> n == 0 ? 1 : n * g:Fac(n - 1) }
echo Fac(6)

If we want to avoid global variables there are two issues:

  1. Fac needs to be "dummy defined" for any closure to take effect.

  2. At script level, the function is called s:Fac. Although it will work inside a function, with local variables.

It is possible to do this at script scope using s: as follows:

let s:Fac = { n -> 1 }
let s:Fac = { n -> n == 0  ? 1 : n * s:Fac(n - 1) }
echo s:Fac(6)

Or at local function scope:

function DoSomething()
    let Fac = { n -> 1 }
    let Fac = { n -> n == 0 ? 1 : n * Fac(n - 1) }
    echo Fac(6)                                                                
endfunction
call DoSomething()   

vim9script improves this situation somewhat, since it allows "declaring" variables by type annotation. It also works at script scope.

vim9script                                                                       
                                                                               
var Fac: func(number): number                                                    
Fac = (n: number): number => {                                                   
    return n == 0 ? 1 : n * Fac(n - 1)                                         
}
4
  • What a bizarre behaviour... is it still possible to define Fac at the script level, or must I be declaring with function keyword?
    – Futarimiti
    Nov 4, 2022 at 10:40
  • 1
    Yes, but for syntax reasons you must use g: or s: as follows: let Fac = { n -> 1 } | let Fac = { n -> n == 0 ? 1 : n * g:Fac(n - 1) } | echo Fac(6) ~OR~ let s:Fac = { n -> 1 } | let s:Fac = { n -> n == 0 ? 1 : n * s:Fac(n - 1) } | echo s:Fac(6)
    – Mass
    Nov 4, 2022 at 22:57
  • Tested in ex mode, it looks like vim is comfortable even without the dummy in this way: :echo Fac; E121: Undefined variable: Fac; :let Fac = { n -> n == 0 ? 1 : n * g:Fac(n - 1) }; :echo Fac(8); 40320
    – Futarimiti
    Nov 5, 2022 at 10:16
  • @Futarimiti, you're right this works because the function is global and has late binding. I have updated my answer accordingly.
    – Mass
    Nov 6, 2022 at 2:55
1

I'm not sure if it's worth to do but, at least, it's worth to mention that we can also parse the call stack to get lambda's name. And so we have

" Calling the caller
function! Self(...) abort
    return expand('<stack>')->split('[\d\+\]\.\.\|\s')[-2]->call(a:000)
endfunction

" will print 720
:echo call({ n -> n <= 1 ? 1 : n * Self(n - 1) }, [6])
1
  • Not exactly what I'm looking for but really innovative idea :) +1
    – Futarimiti
    Nov 5, 2022 at 14:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.