読者です 読者をやめる 読者になる 読者になる

Vim script の funcref() を用いても関数の closure を使用するべきではない

Vim

タイトルがすでに結論なのですが、この前追加された funcref() を使用することで、以下のような問題を回避することが出来ます。

secret-garden.hatenablog.com

function! Foo()
  let x = 0
  function! Bar() closure
    let x += 1
    return x
  endfunction
"   return function('Bar')
  " function() ではなくて funcref() を使用することで
  " 関数が上書きされた場合の問題を回避する
  return funcref('Bar')
endfunction

let F = Foo()
echo F()
" => 1
echo F()
" => 2
echo F()
" => 3

" Bar() 関数が書き換えられても問題ない
let F2 = Foo()

" F() はそのまま動作する
echo F()
" => 4
echo F2()
" => 1
echo F()
" => 5

ではこれで問題ないのか?

確かに funcref() を使用することで問題になっていたコードは意図した動作をするようになりました。
しかし、 funcref() を利用しても依然として、

  • 関数外で参照できる関数を定義してしまう
  • 既存の関数を上書きしてしまう

と、いう問題は残ったままです。
斗くんい funcref() を使用しても『関数を上書きしてしまう』という問題は解決していません。
このような問題が解決しない限りは Vim script でこのような目的の closure は使用するべきではないと個人的に考えています。 Vim script で closure を利用したいのであれば素直にラムダを使うのがよいでしょう。

function! Foo()
  let x = 0
  return { -> [execute("let x += 1"), x][-1] }
endfunction

let F = Foo()
echo F()
" => 1