VimL Functions

Jul 12, 2017

I’ve started a slow descent into the madness that is VimL. One of the things that I’ve found to be initially confusing is how Vim deals with functions. This may be very obvious to some but It caused me enough head scratching to warrant recording some thoughts here.

Cheatsheet

" All VimL functions must be called" Operations like assignment" or passing to another function or built in" implicitly call a functionlet var = MyFunc()
echo MyFunc()" Otherwise, you _must_ :call a functioncall MyFunc()" You can also store a reference to your functionlet FuncRef =function('MyFunc')" and :call it with arguments or pass it to another functioncall(FuncRef,1,2,3)" Or, stringify and execute it
execute 'echo '. string(FuncRef)" Bonus: Lambdas (vim8+ and neovim) are pretty swanklet MyLambda ={ str -> str .'!'}
echo MyLambda('yay')" yay!
echo map([1,2,3],{ _, val -> val * 2})" [2, 4, 6]

Getting func-y

Having one way to call a function is boring: most languages have a few different ways to invoke a function you or someone you love has defined. Not to be outdone, VimL has some twists of its own related to functions.

Let’s create a very simple function:

" All Viml functions must begin with a capitol letterfunction! Hello(name)" :wave: Just in case you were wondering" all named arguments are only available on the magical" arguments dictionary (a) within the functionreturn"Hi ".a:name."!"endfunction

The easiest way to call this function in a script, we could simply assign it to a variable, or pass the result of invoking it to a built in like echo:

This makes a lot of sense! We’ve always been told that VimL doesn’t make much sense; it feels good to prove people wrong doesn’t it?

But, let’s say we just want the side effects of a function and do not want to deal with whatever it returns. It’d make sense to do the same thing but just not assign it right?

MySideEffectFunc('some side effecty argument')
E492: Not an editor command: MySideEffectFunc()

Not so fast! VimL has other ideas; while certain built in commands (like echo) can be invoked, functions cannot simply be called without passing or assigning their value. This is because things like echo Foo() and let x = Foo() implicitly evaluate or call any expression they are handed (in this case, the expression being invoking the function Foo). Since Foo() isn’t good enough, we need a way to tell VimL to actually call the function.

This is where :call steps in. :call calls a function, with up to 20 arguments (because 19 just wasn’t enough), and discards its return value.

:call MySideEffectFunc('this wooooorks')

Call is the the way of calling functions within your plugins, or invoking other functions from ex mode.

Show me your references

Let’s explore another way we can use our functions: references. Let’s take a common example, using map with a function we’ve previously defined. We can use Vim’s function keyword to create a funcref (that is a reference to function winkwink) which allows us to pass it to map, filter, or another function.

If we wanted to take a more generic function that did not need to be aware of idx, we could use stringify our funcref and use map’s second argument (a string to be evald) to invoke our function with the value of each pair we are iterating through:

That’s it!

There’s so much under the hood with VimL but effectively using functions is a great way to get started writing a simple script or plugin to help make your life better. I hope this helps remove some of the confusion that I initially had.