0

These exist with lambdas

function A()

  let B = { -> 'thing' }

endfunction

// B does no exist

But the lambda returns a value and not a sequence of commands.

(I know I could put execute("..") in the lambda but this is an ugly hack.)

function A()

  function B()
    echo "I'm going to leak"
  endfunction

endfunction

// B exists everywhere now

Now we can call B() from outside A, so it's not local to the function.

So, is it possible to have a function-local inner functions?

(I want to pass closure callbacks without redefining a script-local function every time.)

2
  • I wonder if dictionary functions would work? Alternate parameterize the body as a function and return a lambda invoking the function.
    – D. Ben Knoble
    Jul 27, 2021 at 1:08
  • I was thinking lambdas are not evaluated until called.
    – Maxim Kim
    Jul 27, 2021 at 6:11

2 Answers 2

2

One way to prevent creation of a global function is to use "dictionary entry funcref syntax." Note that this is distinct, but also useful paired with, functions which are marked dict.

function A()
  let l:container = {}

  function container.myfunc()
    echo "I'm going to leak"
  endfunction

  call l:container.myfunc()
  return l:container.myfunc
endfunction

Internally, vim is creating a "numbered" function stored in a funcref type variable. This is very similar to how a lambda is implemented. Although you can return the funcref value and safely call it outside, once all references are gone, it is cleaned up.

You can also use the closure keyword to allow use of outside variables, like is the default for a lambda. Use care, because it is rather easy to create circular references in this way, and vim will not clean up resources if references exist.

function A()
  let l:value = 10
  let l:container = {}

  function container.myfunc() closure
    echo "Variable" l:value
  endfunction

  call l:container.myfunc()
endfunction
2
  • Thanks. I guess it's a little ugly but it's exactly what I want.
    – mmm111mmm
    Jul 27, 2021 at 11:05
  • Regarding another comment, the above function can work as a closure too.
    – mmm111mmm
    Jul 27, 2021 at 11:58
3

Yes, of course.

function! Outer() abort
    let l:time = strftime("%c")

    function! s:inner(param) abort closure
        echom "Inner closure was created at" l:time
        echom "Param is" a:param
    endfunction

    return funcref("s:inner")
endfunction

let g:Inner = Outer()
call g:Inner(42)
6
  • 1
    Nice! I always forget about closure clause.
    – Maxim Kim
    Jul 27, 2021 at 6:53
  • s:inner can be called outside Outer. So it's not local to the function. I'll make my post clearer.
    – mmm111mmm
    Jul 27, 2021 at 10:56
  • @mmm111mmm In VimScript any function can be called if you know its name.
    – Matt
    Jul 27, 2021 at 11:01
  • @Matt See the accepted answer. Mass has an interesting solution
    – mmm111mmm
    Jul 27, 2021 at 11:03
  • @mmm111mmm It's not a closure.
    – Matt
    Jul 27, 2021 at 11:36

Your Answer

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

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