Darshan Sawardekar
Darshan Sawardekar

Reputation: 5075

Pass local variables to a function created inside eval ie:- Closures in viml

I am creating a dynamic function at runtime inside an execute/eval'd string. The function is built from different variables present at the time the generator function is called. The generator function is below,

function! NewCallback(method, opts) 
  let my_opts = a:opts
  let function_name = "g:MyDynamicFunction_" . a:method

  let body  = "function! " . function_name . "(...)\n"
  let body .= "  echo 'running " . function_name . "'\n"
  let body .= "  echo my_opts\n"
  let body .= "endfunction"

  execute body
  return function_name
endfunction

I am using this function like below. First I create a callback which gives the name of the function generated as a result. I then call this function with :call.

let callback = NewCallback('foo', { 'a': 1, 'b': 2 })
execute(":call " . callback . "(1, 2, 3)")

The problem I am having is accessing variables in the scope of NewCallback inside the generated function. Here I need to access my_opts which is local to NewCallback inside the generated function MyDynamicFunction_foo.

Currently it gives me undefined variable my_ops

Is there a way to do this in viml. ie:- Defining closure functions that can access parent scope?

Thanks.

Upvotes: 4

Views: 672

Answers (1)

elmart
elmart

Reputation: 2374

You can have something like closures in vimscript. They're called dictionary functions. Those are functions associated to a dictionary acting like an implicit context for them. Within those functions, implicit context can be accesed through the keyword self. So, they're just like object methods. See here for a better explanation.

Using them, you can code your sample like this, for example:

function! NewListener(method, opts) 
  let context = {'name': a:method, 'opts': a:opts}

  function! context.f(...)
    echo "running " . self.name . "\n"
    echo self.opts
    echo "\n"
  endfunction

  return context
endfunction

let listener = NewListener('foo', { 'a': 1, 'b': 2 })
call listener.f(1, 2, 3)

Upvotes: 6

Related Questions