C# 3.0 のラムダの負の側面

http://diditwith.net/2007/09/25/LINQClosuresMayBeHazardousToYourHealth.aspx

問題

まず、上記 URL の(前半部分の)要点だけ抜き出すと、

var filter = "Compare";
var query = from m in typeof(String).GetMethods()
            where m.Name.Contains(filter)
            select new { m.Name, ParameterCount = m.GetParameters().Length };

filter = "IndexOf";
foreach (var item in query)
  Console.WriteLine(item); 

みたいなコードを書いたときに、(Compare の方じゃなく)IndexOf を含むメソッド一覧が列挙されるという話。

要するに、↑のコードの2行目、

  where m.Name.Contains(filter)

の m.Name.Contains(filter) の部分は、匿名メソッドになるんですが、この匿名メソッドはローカル変数 var filter の参照を持ってる状態。
参照なんで、filter = "IndexOf"; に書き換えると、m.Name.Contains(filter) の方の filter も変化します。で、実際に、where の選択処理が実行されるのは foreach のところなので、変化した後の "IndexOf" を含むメソッド一覧が得られる。

問題の原因

混乱の原因は、やっぱり命令型言語である C#関数型言語の構文を輸入してるせいだと思うんですけど。
C#ラムダ式に、関数型言語の Closure 的な動作を期待しすぎるから変な思い違いするんじゃないかと。
(こういう混乱、関数型言語の場合は、変数が immutable なんで起こらないんですけど、C#ラムダ式だと変数は mutable なんで。)
自分としては、C#ラムダ式ってのは匿名メソッドの拡張あるいは構文糖だと思ってて、関数型言語と同じノリで扱う気はないんで、↑みたいな動作もすごく当然な動作なんですけど。

言語仕様はこれでいい?

C# の言語設計者的には、クエリ式中で↑みたいな状態が変化する(副作用のある)コードを書くのはあんまり想定してないというか、非推奨というか、「副作用起こせるように言語仕様は作ったけど、クエリ式の辺りは関数型言語パラダイムにそって副作用のない書き方するのがいいと思うな」みたい姿勢でクエリ式構文作ってるような雰囲気を感じる。
でも、個人的には、そういう仕様でよかったように思うんですけどね。
命令型言語の C# の中に関数型言語パラダイムを混ぜ込むのは、やっぱり余計大きな混乱起こすと思う。今みたいに、一見関数型言語から機能を輸入したように見える構文も、動作的には命令型言語のパラダイムに沿った動作すべきだと思う。C# 3.0 の新機能に、関数型言語と同じ動作を期待しながらコードを書いちゃダメ。