Blogged by Ujihisa. Standard methods of programming and thoughts including Clojure, Vim, LLVM, Haskell, Ruby and Mathematics written by a Japanese programmer. github/ujihisa

Thursday, November 18, 2010

Something like closure for Vim script

Vim script doesn't have lambda as a language feature, but you can fake similar thing with the following tricks.

When you want to pass a procedure with variables into a function in Vim script, how do you achieve it?

Let me explain with other language first, show a well-known function map.

Ruby(*1):

a = 10
p [1, 2, 3].map {|i| i + a }
#=> [11, 12, 13]

Python:

a = 10
print map(lambda x: x + a, [1, 2, 3])
#=> [11, 12, 13]

or more strictly

a = 10
def f(x):
    return x + a
print map(f, [1, 2, 3])
#=> [11, 12, 13]

On the other hand, Vim script doesn't have such fancy feature but have normal function and dictionary function. The former is like just a global function, scoped globally or file. The latter is like a method which has the concept of this of self in other languages.

let obj = {'a': 10}
function! obj.f(x)
  return a:x + self.a
endfunction

echo obj.f(1)
"=> 11

Note that the prefix a: means the identifier is an argument of the function.

This feature is compatible with lexical scope lambda except for the aspect of the anonymity. But it is very inconvenient that you always have to declare which variables the dictionary function will use.

The solution is to use a special variable l:.

function! Main()
  let a = 10
  let obj = copy(l:)
  function! obj.f(x)
    return a:x + self.a
  endfunction

  echo obj.f(1)
endfunction

call Main()

There are three notes for using this trick:

  • l: is only available in a function.
  • it can cause SEGV if you forget writing copy
  • a: as well if you use not only local variables

Acknowledge

thinca and tyru taught me this trick for my problem presentation. thinca is using a kind of this trick in his product.

Footnote

The following code also works on Ruby.

a = 10
f = ->(x) { x + 1 }
p [1, 2, 3].map(&f)

2 comments:

  1. How about real closures? :-)

    function! Main()
    lua << EOLUA
    local a = 10

    local f = function (x)
    return a
    end

    vim.command('echo "' .. f() .. '"')
    EOLUA
    endfunction

    call Main()

    greetings!

    ReplyDelete
  2. if_*** is always good if I can assume everyone has it :)

    if_gauche also has real closures

    ReplyDelete

Followers