Admit it, you’ve wanted this for a long time now.
Update: These two functions are now available in
VimaholicsAnonymous.
VimL’s list
sort() method allows the caller to provide a
custom comparison function with which to sort by.
Unfortunately, VimL doesn’t support anonymous functions
(sometimes called lambdas), so the caller is forced to
pre-write their comparator as a full-fledged function and
provide its
funcref to
sort(). Well, that ends
today. Now, VimL
has anonymous function love! Check it:
This humble little snippet of code lets vimmers declare
anonymous functions on the fly:
let fn_idx = 0
function! Fn(fn_form)
let [fn_args, fn_body] = split(a:fn_form, '\s*=>\s*')
let g:fn_idx = g:fn_idx + 1
let fname = 'AnonFn_' . g:fn_idx
let b_elems = split(fn_body, '|')
let b_elems[-1] = 'return ' . b_elems[-1]
exe 'func! ' . fname . fn_args . "\n"
\. join(b_elems, "\n") . "\n"
\. 'endfunc'
return function(fname)
endfunction
|
Like this:
let x = ["one","two","three","four","five","six","seven","eight","nine","ten"]
echo sort(x, Fn('(a, b) => len(a:a) > len(a:b)'))
|
The magic is in the
Fn() call. It takes a string
argument of the form:
(arguments) => function-body statements separated by | (pipe)
The last statement will be implicitly returned by the
anonymous function, so no need to explicitly add a
return statement.
Unicorns, or what?!
What? You want more. Certainly, sir. Behold:
function! Fx(fn, ...)
return call(a:fn, a:000)
endfunction
|
That lets you execute an anonymous function, like:
echo '2 ^ 6 = ' . string(Fx(Fn('(a, b) => pow(a:a, a:b)'), 2, 6))
|
Any cooler and you’d need your jumper! :-D
Oh, but there’s more… I’m just getting warmed up!
Just because I like you, here’s a little extra gift:
let Mul = Fn('(a,b)=>let x = a:a | let y=a:b | x*y')
echo '5 * 6 = ' . Fx(Mul, 5, 6)
|
That lets you call your
funcref'd anonymous function
by name. Did you see what I did there? Yes, I cheated God! I
named an annonymous function. Cool, eh?
So, apart from the obvious, is this really that amazing? My
vote is: YES! I have
plans for this technology. Here’s a
sneaky hint of where I’m looking to take this:
CompileMacros
Mul = ((a,b) => let x = a:a | let y=a:b | x*y)
echo '5 * 6 = ' . #(Mul, 5, 6)
echo '2 * 6 = ' . #(((a, b) => a:a * a:b), 2, 6)
|
That’s right. I want hygenic macros in VimL. And that code
works in my current experiments. The notation is stolen
from… clojure I believe, where #(…) executes a lambda.
The call to
CompileMacros will process any macros in the
surrounding expressions before sourcing the result. Just for
completeness, defining macros currently looks like this:
call Macro('(\+\((.\{-})\s*=>.\{-}\))', "Fn('\\1')")
call Macro('\%(\n\|^\)\@<=\s*\(\w\+\)\s*=\s*\(Fn(.*=>.\{-})\)\s*\n',
\ "let \\1 = \\2\n" )
call Macro('#(\(.*\))\n', "Fx(\\1)\n")
call Macro('#Fn(\(.\{-}\))\n', "Fx(Fn(\\1)\n")
|
But that will change in the next version. Using regex to
parse is an abomination; I was merely proof-of-concepting
here. I’ll use
VimPEG instead.
Stay tuned, if bending the spoon is what you’re in to.