本当にキモい Vim script - . 演算子編

Vim Advent Calendar 2012 の 362 日目の記事です。
Vim Advent Calendar 2012 も残すところあと4日ですね。寂しいものです。


今回は前回やったキモいシリーズの第2回。
突然だけど、以下の関数はどのような挙動をするだろうか。

function! s:func(time)
  let time = a:time
  let suffix = 'min'
  return 1000-time.suffix
endfunction

つまりこういうことか?「time という数値を受け取り、1000からその数値を引き、末尾に 'min' と言うサフィックスを付けた文字列を返す…」






Exactly(そのとおりでございます)

echo s:func(50)
" => 950min


では、以下のような値を渡すとどうなるか…

echo s:func({'suffix': 'sec'})
" => 1000

!?

何が起きたのか

return 文の部分を評価される順に () を付けていってみよう。
まずは 50 を渡した場合。

return ((1000-time).suffix)

文字列結合演算子 "." より減算演算子 "-" の方が優先順位が高いため、こうなる。
続いて後者。

return (1000-(time.suffix))

time は辞書になる。減算演算子 "-" より辞書アクセズ演算子 "." の方が優先順位が高いため、こうなる。*1


なんと、評価順序が変わっている!


これは、. 演算子が、左辺の値が文字列か辞書かで意味が変わるため。毎回実行時に動的に変わる。
このような挙動は、静的型付け言語はもちろん、実行時に内部でコンパイルしてASTを生成するような言語でも不可能!*2Vim script すごい。

まとめ

Vim script キモい。



ちなみにキモいシリーズは今のところもうネタがないので、続きを書くかどうかは不明です。

*1:ちなみに、文字列に算術演算子を適用すると数値に変換され、変換できない場合は 0 になるので、1000 - 0 で結果は 1000 になる

*2:evalを使った場合は除く